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()