本篇文章于 823 天前发表,某些内容可能已经过时,请注意甄别。

以下程序输出结果是____

c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A{
public:
A ():m_iVal(0){test();}
virtual void func() { std::cout<<m_iVal<<' ';}
void test(){func();}
public:
int m_iVal;
};
class B : public A{
public:
B(){test();}
virtual void func(){
++m_iVal;
std::cout << m_iVal << ' ';
}
};
int main(int argc ,char* argv[]){
A*p = new B;
p->test();
return 0;
}

A. 1 0 B. 0 1 C. 0 1 2 D. 不可预期

本问题涉及到两个方面:

  1. C++ 继承体系中构造函数的调用顺序:

    (1) 任何虚拟基类的构造函数按照他们被继承的顺序构造

    (2) 任何非虚拟基类的构造函数按照他们被继承的顺序构造

    (3) 任何成员对象的函数按照他们声明的顺序构造

    (4) 类自己的构造函数

    需要注意,成员对象在进入类构造函数的函数体前就已经完成了初始化 (初始化列表中),函数体内的所有参数都只算赋值。

  2. 构造函数中调用虚函数问题:

    在构造或析构函数中调用虚函数会执行与之所属类型相对应的虚函数版本。

    原因如下:

    假设构造函数中调用虚函数,其会表现为相应的子类函数行为,并且假设子类存在一个成员变量 int a;子类定义的虚函数的新的行为会操作 a 变量,在子类初始化时根据构造函数调用顺序会首先调用父类构造函数,那么虚函数回去操作 a,而因为 a 是子类成员变量,这时 a 尚未初始化,这是一种危险的行为,作为一种明智的选择应该禁止这种行为。