先看一个例子
#include <iostream>
class itemA {
public:
itemA() {std::cout << "itemA void constructor" << std::endl;}
itemA(int m) {std::cout << "itemA param constructor" << std::endl;}
};
class itemB {
public:
itemB() {std::cout << "itemB void constructor" << std::endl;}
itemB(int m) {std::cout << "itemB param constructor" << std::endl;}
itemB& operator=(int m) {std::cout << "itemB param assign" << std::endl;return *this;}
};
class base1 {
public:
base1() {std::cout << "base1 void constructor" << std::endl;}
explicit base1(int x) {std::cout << "base1 param constructor" << std::endl;}
};
class base2 {
public:
base2() {std::cout << "base2 void constructor" << std::endl;}
explicit base2(int x) {std::cout << "base2 param constructor" << std::endl;}
};
class derived : public base1, base2 {
public:
derived() {std::cout << "derived void constructor" << std::endl;}
derived(int m) : a(m), base2(m){b = 10;std::cout << "derived param constructor" << std::endl;}
private:
itemB b;
itemA a;
};
int main() {
derived d(3);
return 0;
}
输出
base1 void constructor
base2 param constructor
itemB void constructor
itemA param constructor
itemB param assign
derived param constructor
结论如下
基类构造函数 > 成员构造函数 > 自身构造函数,至于初始化列表,并不影响构造顺序。
- 基类的构造顺序只和继承类声明顺序相关,先声明的先执行,初始化列表并不会影响此执行顺序。但想要手动调用基类构造只能写在初始化列表中,因为在进入派生类构造代码块之前,完成基类的构造这一约束条件必须被满足(这一约束对成员构造也成立)。
- 成员的构造顺序只和成员类声明顺序有关,先声明的先执行,初始化列表并不会影响此执行顺序。
- 如果打算为一个非基本内置类型的成员变量赋初值,将赋值操作写在初始化列表中要比写在构造函数代码块里面效率更高,这是因为即使写在代码块中也会在进入代码块之前先调用此成员的默认构造,进入代码块后再调用此成员的拷贝构造函数或拷贝赋值函数(如果存在);而直接写在初始化列表只需要调用一次拷贝构造即可。