9.3.2 声明类的保护成员
类成员除了有public和private访问说明符以外,我们还可以将类成员声明为protected。在类的内部,protected关键字与private关键字具有相同的效果。类的保护成员只能被类的成员函数和类的友元函数访问(还能被友元类的成员函数访问-- 本章稍后将讨论友元类)。使用protected关键字,我们可以将CBox类重新定义成如下形式:
- // Box.h in Ex9_04
- #pragma once
- #include <iostream>
- using std::cout;
- using std::endl;
-
- class CBox
- {
- public:
- // Base class constructor
- CBox(double lv = 1.0, double wv = 1.0, double hv = 1.0):
- m_Length(lv), m_Width(wv), m_Height(hv)
- { cout << endl << "CBox constructor called"; }
-
- // CBox destructor - just to track calls
- ~CBox()
- { cout << "CBox destructor called" << endl; }
-
- protected:
- double m_Length;
- double m_Width;
- double m_Height;
- };
现在,3个数据成员仍然不能被普通的全局函数访问,从这方面来讲,它们实际上还是私有成员,但可以被派生类的成员函数访问。
试一试:使用保护成员
通过使用该版本的CBox类来派生CCandyBox类的新版本,并让CCandyBox类利用自己的成员函数Volume()访问基类的成员,我们就可以证实protected数据成员的用途。
- // CandyBox.h in Ex9_04
- #pragma once
- #include "Box.h"
- #include <iostream>
- using std::cout;
- using std::endl;
-
- class CCandyBox: public CBox
- {
- public:
- char* m_Contents;
-
- // Derived class function to calculate volume
- double Volume() const
- { return m_Length*m_Width*m_Height; }
-
- // Constructor to set dimensions and contents
- // with explicit call of CBox constructor
- CCandyBox(double lv, double wv, double hv, char* str = "Candy")
- :CBox(lv, wv, hv) // Constructor
- {
- cout << endl <<"CCandyBox constructor2 called";
- m_Contents = new char[ strlen(str) + 1 ];
- strcpy_s(m_Contents, strlen(str) + 1, str);
- }
-
- // Constructor to set contents
- // calls default CBox constructor automatically
- CCandyBox(char* str = "Candy") // Constructor
- {
- cout << endl << "CCandyBox constructor1 called";
- m_Contents = new char[ strlen(str) + 1 ];
- strcpy_s(m_Contents, strlen(str) + 1, str);
- }
-
- ~CCandyBox() // Destructor
- {
- cout << "CCandyBox destructor called" << endl;
- delete[] m_Contents;
- }
- };
Ex9_04.cpp文件中的main()代码如下:
- // Ex9_04.cpp
- // Using the protected access specifier
- #include <iostream> // For stream I/O
- #include <cstring> // For strlen() and strcpy()
- #include "CandyBox.h" // For CBox and CCandyBox
- using std::cout;
- using std::endl;
-
- int main()
- {
- CCandyBox myCandyBox;
- CCandyBox myToffeeBox(2, 3, 4, "Stickjaw Toffee");
-
- cout << endl
- << "myCandyBox volume is " << myCandyBox.Volume()
- << endl
- << "myToffeeBox volume is " << myToffeeBox.Volume();
- // cout << endl << myToffeeBox.m_Length;
// Uncomment this for an error -
- cout << endl;
- return 0;
- }
示例说明
在该示例中,我们通过调用派生类的成员函数Volume(),来计算两个CCandyBox对象的体积,该函数通过访问继承的成员m_Length、m_Width和m_Height而获得结果。这些成员在基类中被声明为protected,在派生类中仍然是protected。该程序产生下面的输出:
- CBox constructor called
- CCandyBox constructor1 called
- CBox constructor called
- CCandyBox constructor2 called
- myCandyBox volume is 1
- myToffeeBox volume is 24
- CCandyBox destructor called
- CBox destructor called
- CCandyBox destructor called
- CBox destructor called
输出证实,两个CCandyBox对象的体积计算是正确的。第一个对象具有通过调用默认CBox构造函数获得的默认尺寸,因此其体积是1。第二个对象的尺寸是由声明中的初值确定的。
输出同时显示出构造函数和析构函数的调用次序,可以看出每个派生类对象都是分两步被销毁的。
注意:
派生类对象析构函数的调用顺序与构造函数相反。这是一条普遍适用的规则。创建对象时首先调用基类的构造函数,然后调用派生类的构造函数;而销毁对象时首先调用派生类的析构函数,然后才调用基类的析构函数。
通过解除main()函数中return语句前面那条语句的注释状态,我们可以证明基类的protected成员在派生类中仍然是protected。如果那样做,我们将得到下面这条来自编译器的出错消息:
- error C2248: 'm_Length': cannot access
protected member declared in class 'CBox'
该消息非常清楚地指出,m_Length成员是不可访问的。