子类构造函数必须在成员初始化列表中显式调用父类构造函数,否则编译器尝试调用父类默认构造函数;若父类无默认构造函数则报错,且调用不可在函数体内进行,多继承和虚继承时也须逐一或由最派生类负责调用。

子类构造函数里必须显式调用父类构造函数
不写的话,编译器会尝试调用父类的默认构造函数(无参、且必须存在)。如果父类没定义默认构造函数,g++ 或 clang++ 会直接报错:no matching constructor for initialization of 'Base'。
正确做法是在子类构造函数的成员初始化列表中,用 Base(...) 显式调用:
class Base {
public:
Base(int x) : val(x) {}
private:
int val;
};
class Derived : public Base {
public:
Derived(int x, int y) : Base(x), data(y) {} // 必须在这里调用 Base(x)
private:
int data;
};
- 调用位置只能在成员初始化列表,不能放到构造函数体内部(否则父类对象早已默认构造完毕,再“重构造”不合法)
- 如果父类有多个构造函数,选哪个由你传入的参数决定;类型不匹配会导致编译失败,比如传
double却只定义了Base(int) - 即使父类有默认构造函数,也建议显式写出
Base()——避免后续删掉默认构造时漏改子类,引发隐晦编译错误
多继承时每个基类都要单独调用
当子类同时继承多个父类,每个基类的构造函数都得在初始化列表中列出,顺序无关(但实际构造顺序按继承声明顺序执行,和初始化列表顺序无关)。
例如:
立即学习“C++免费学习笔记(深入)”;
class A { public: A(int) {} };
class B { public: B(double) {} };
class C : public A, public B {
public:
C(int a, double b) : A(a), B(b) {} // 两个都得写,缺一不可
};
- 漏掉任意一个,就会触发对应基类的默认构造尝试,大概率失败
- 若某基类是虚继承(
virtual public A),仍需调用,且**最派生类**(这里是C)负责调用它,中间继承链上的调用会被忽略 - 虚基类构造函数调用优先级最高,哪怕写在初始化列表末尾,也会最先执行
委托构造函数不能绕过父类调用
即使子类用了委托构造(即一个构造函数调另一个),父类构造函数仍必须出现在最终被调用的那个构造函数的初始化列表中。
class D : public Base {
public:
D(int x) : Base(x + 1) {} // ✅ 正确:父类调用在此处
D() : D(42) {} // ✅ 委托合法,但 D(42) 已含 Base 调用
// D() : Base(0) {} // ❌ 错误:委托构造函数体内不能再有初始化列表
};
- 委托构造函数的初始化列表必须为空,所有基类/成员初始化必须落在被委托的目标构造函数里
- 试图在委托构造函数里加
Base(...)会编译报错:member initializer list is not allowed in a delegating constructor - 这个限制常被忽略,尤其在重构多个构造函数时容易出错
父类成员变量不能在子类构造函数体里“重新赋值”来替代构造调用
有人想省事,在子类构造函数体里写 base_member = value;,这是无效的——父类部分在进入函数体前已构造完成,此时只是给已有对象赋值,不是初始化。
更危险的是:如果父类成员是 const 或引用类型,根本无法在函数体内赋值,只能靠初始化列表。
-
const int x;在父类中?那必须在Base(...)初始化列表里初始化,子类无法干预其值,除非通过父类构造函数参数间接控制 - 父类有
std::mutex这类不可复制/不可赋值的成员?那就更依赖构造时正确初始化,运行时赋值直接编译失败 - 性能上,初始化列表是直接构造,函数体内赋值是先默认构造再赋值,多一次开销(对复杂类型可能明显)










