C++ 构造函数漫谈(三)
这一章聊一聊在面向对象的C++中,构造函数的调用顺序。
数据成员的构造顺序
一个类的数据成员的初始化顺序只与其在类中的声明顺序相关,与其它无关。
而析构时,如果成员是在堆中,析构顺序正好与构造时相反。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
class M { public: M(const char* msg) { cout << "M " << msg << endl; msg_ = new char[strlen(msg)+1]; msg_[strlen(msg)] = '\0'; strcpy(msg_, msg); } ~M() { cout << "~M " << msg_ << endl; delete msg_; } private: char* msg_; }; class A { public: A() : pm2_(new M("pm2")), m2_("m2"), pm1_(new M("pm1")), m1_("m1") {} private: M m1_; M m2_; M* pm1_; M* pm2_; }; int main() { A a; return 0; } |
类A的成员的构造顺序为: m1_, m2_, pm1_, pm2_
。析构时的顺序为 m2_, m1_
,由于 pm1_, pm2_ 不在堆中,所以它们的析构需要类A自己管理。
1 2 3 4 5 6 |
M m1 M m2 M pm1 M pm2 ~M m2 ~M m1 |
继承, 多重继承,虚继承中父类的构造顺序
在简单继承中,子类在构造之前总是先调用父类的构造函数,而析构顺序完全相反。
在多重继承中,父类构造的顺序与继承声明的顺序相关,与子类调用无关。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class A { public: A() { cout << "A()" << endl; } }; class B { public: B() { cout << "B()" << endl; } }; class D1 : public B { public: D1() { cout << "D1()" << endl; } }; class D2 : public B { public: D2() { cout << "D2()" << endl; } }; class E :public A, public D1, public D2 { public: E():D2(),D1(), A() { cout << "E()" << endl; } }; int main() { E e; return 0; } |
如上代码中,尽管 class E
中我们显式地构造父类,但父类的构造顺序依然是: A, B, D1, B, D2
1 2 3 4 5 6 |
A() B() D1() B() D2() E() |
这里有一个特殊的继承关系:B, D1, D2, E
这种菱形继承,使得对象 D1 和 D2 内都各有一个 B 类子对象,即 类 E 的对象中有一两个 B 类子对象。为防止这种情况我们一般使用 虚继承,使得 E 的对象内只有一个 B类子对象。在有虚继承的多重继承中,虚继承会优先完成构造。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class A { public: A() { cout << "A()" << endl; } }; class B { public: B() { cout << "B()" << endl; } }; class D1 : virtual public B { public: D1() { cout << "D1()" << endl; } }; class D2 : virtual public B { public: D2() { cout << "D2" << endl; } }; class E :public A, public D1, public D2 { public: E():D2(),D1(), A() { cout << "E()" << endl; } }; int main() { E e; return 0; } |
如上代码, 类 B 的构造函数会被最先调用且只会调用一次,然后再其它类按照一般情况完成构造。
1 2 3 4 5 |
B() A() D1() D2 E() |