类的继承中的一些细节问题
请阅读如下程序
class A {
public:
A() :a(0) {}
~A() {
a1 -= a;
}
private:
int a;
protected:
static int a1;
};
class B :public A{
public:
B() :A() {}
~B(){}
};
int A::a1 = 0;
int main() {
B* b1 = new B;
delete b1;
return 0;
}
在这段程序中有一个隐藏的问题:A类的两个数据成员,a的访问限定符为private,a1的访问限定符为protected。故a在B类中不可访问,a1在B类中可以访问。那么在执行B* b1 = new B; 这条语句时,应当是通过B类的构造函数调用A类的构造函数。那么程序是否会跳过B类不可访问的a成员的初始化而只执行a1成员的初始化呢?
如果程序没有初始化a,那么在执行析构时,在执行到a1-=a; 的这条语句时就会发生错误,因为此时a并没有被初始化就被直接使用。程序的执行结果是,该条语句执行正常,a的值和a1的值都被正常初始化为0。这说明,在类的继承中,派生类调用基类的构造函数,而基类的构造函数正常对所有其初始化的成员进行初始化,而不会跳过派生类中不可访问的成员,只不过private的成员在派生类中不可访问罢了。之前在某些技术博客中看到了所谓“会忽略派生类中不可访问的成员”的说法应该是错误的。
这有时候会导致一些问题。请看下面的代码:
class A {
public:
A() :a(0),a1(0){}
~A() {
a1 -= a;
a2--;
}
private:
int a;
static int a2;
protected:
int a1;
};
class B :public A{
public:
B() :A() {}
~B(){}
};
int A::a2 = 0;
int main() {
B* b1 = new B;
delete b1;
return 0;
}
假设我们希望static int A::a2是一个可以记录程序中A类成员个数的计数器。因此我们在A类的析构函数中写入的a2–; 的语句。但是当我们定义了一个B类的对象,并删除该对象时,会调用A类的析构函数,会正常执行a2–; 的语句。A类的对象并没有减少,而A类对象的计数器却减少了一个,这显然是不行的。解决方法是,我们将a2声明为protected,然后在B类的析构函数中先把a2++; 这样就通过一种暴力的方法解决了问题。
代码如下
class A {
public:
A() :a(0),a1(0){}
~A() {
a1 -= a;
a2--;
}
private:
int a;
protected:
int a1;
static int a2;
};
class B :public A{
public:
B() :A() {}
~B(){
a2++;
}
};
int A::a2 = 1;
int main() {
B* b1 = new B;
delete b1;
return 0;
}