从 C++ 托管到 Visual C++,类构造函数的初始化顺序已经发生变化。
构造函数初始化顺序的比较
在 C++ 托管扩展中,构造函数的初始化是按以下顺序进行的:
-
如果存在基类的构造函数则调用该构造函数。
-
计算该类的初始化列表。
-
执行类构造函数的代码正文。
此执行顺序与本机 C++ 编程遵守相同的约定。 新 Visual C++ 语言规定了 CLR 类的执行顺序,如下所示:
-
计算该类的初始化列表。
-
如果存在基类的构造函数则调用该构造函数。
-
执行类构造函数的代码正文。
上面的文字用代码表示就是:
__gc class A { public: A() : _n(1) { } protected: int _n; }; __gc class B : public A { public: B() : _m(_n) { } private: int _m; }; 按照上述的构函数初始化顺序,在构造类 B 的新实例,我们应看到如下的执行顺序: 1.调用基类 A 的构造函数。 将 _n 成员初始化为 1。 2.计算类 B 的初始化列表。 将 _m 成员初始化为 1。 3.执行类 B 的代码正文。 现在请考虑以新 Visual C++ 语法表示的相同代码: ref class A { public: A() : _n(1) { } protected: int _n; }; ref class B : A { public: B() : _m(_n) { } private: int _m; }; 按照新语法构造的类 B 的新实例的执行顺序如下: 1.计算类 B 的初始化列表。 将 _m 成员初始化为 0(0 是 _m 类成员的未初始化值)。 2.调用基类 A 的构造函数。 将 _n 成员初始化为 1。 3.执行类 B 的代码正文。 请注意,对于这些代码示例,相似的语法产生了不同的结果。 类 B 的构造函数依赖于基类 A 的值来初始化其成员。 但并未调用类 A 的构造函数。 如果需要分配内存或资源才能在基类构造函数中生成继承类,则此种依赖关系将是非常危险的。
另外更为详细的构造顺序如下:
C++构造函数按下列顺序被调用:
(1)任何虚拟基类的构造函数按照它们被继承的顺序构造;
(2)任何非虚拟基类的构造函数按照它们被继承的顺序构造;
(3)任何成员对象的构造函数按照它们声明的顺序调用;
(4)类自己的构造函数。#include <iostream> using namespace std; class OBJ1 { public: OBJ1(){ cout <<"OBJ1\n"; } }; class OBJ2 { public: OBJ2(){ cout <<"OBJ2\n"; } }; class Base1 { public: Base1(){ cout <<"Base1\n"; } }; class Base2 { public: Base2(){ cout <<"Base2\n"; } }; class Base3 { public: Base3(){ cout <<"Base3\n"; } }; class Base4 { public: Base4(){ cout <<"Base4\n"; } }; class Derived :public Base1, virtual public Base2, public Base3, virtual public Base4 { public: Derived() :Base4(), Base3(), Base2(), Base1(), obj2(), obj1() { cout <<"Derived ok.\n"; } protected: OBJ1 obj1; OBJ2 obj2; }; int main() { Derived aa; cout <<"This is ok.\n"; int i; cin >> i; return 0; } 结果: Base2 Base4 Base1 Base3 OBJ1 OBJ2 Derived ok. This is ok.
分析以上输出结果可以得出以下结论:在被继承的基类中,虚基类优先级最高,其次是普通的基类,然后是本类的成员函数按照顺序构造。在所有的虚基类中按照被继承的顺序对各个类中成员进行初始化。同样在所有的普通基类中按照被继承的顺序对各个类中成员进行初始化。