子类必须在初始化列表中显式调用父类构造函数,如derived(int x) : base(x + 1) {};不可在函数体内用base(x)调用,否则创建临时对象;虚函数表指针在构造函数体执行前初始化,故构造/析构中虚函数调用不具多态性;纯虚函数可带函数体以提供默认实现。

继承时子类怎么调用父类构造函数
子类对象创建时,父类部分必须被初始化,但编译器不会自动帮你传参——你得显式写出来。不写的话,只会在父类有默认构造函数时“侥幸通过”;一旦父类只有带参构造函数,error: no matching function for call to 'Base::Base()' 就立刻报给你看。
- 在子类构造函数的初始化列表里调用父类构造函数,比如
Derived(int x) : Base(x + 1) {} - 不能在子类构造函数体内用
Base(x)这种写法——那只是临时创建一个匿名Base对象,跟当前对象的父类部分完全无关 - 如果父类构造函数是
explicit的,子类初始化列表里也必须严格匹配,不能依赖隐式转换
虚函数表指针(vptr)什么时候被写入对象内存
不是编译期决定,也不是运行期任意时刻——而是在构造函数执行「进入函数体之前」由编译器悄悄插入初始化代码。这意味着:在父类构造函数里调用虚函数,哪怕子类重写了,也只会调用父类版本。
- 原因很简单:此时子类部分还没开始构造,vptr 指向的是父类的虚函数表,不是最终的派生类表
- 同理,在析构函数里调用虚函数,也会绑定到当前正在析构的那个类的版本,而不是更派生的版本
- 所以别在构造/析构函数里做虚函数分发逻辑,容易误以为“多态生效了”,其实只是静态绑定
为什么纯虚函数定义成 = 0 却还能有函数体
= 0 只是语法标记“该函数必须被派生类实现”,并不禁止你给它加函数体。有体的纯虚函数,常用于提供默认实现,同时强制接口契约。
- 定义带函数体的纯虚函数:
virtual void draw() = 0 { std::cout - 派生类仍可直接调用父类实现:
Base::draw(),只要它没被声明为private - 但注意:含纯虚函数的类是抽象类,不能实例化;哪怕所有纯虚函数都有函数体,也不能绕过这条规则
override 关键字不是可选的装饰,而是编译期检查开关
不写 override,编译器就当你是想定义一个新函数——哪怕函数签名和父类虚函数只差一个 const 或引用符,也会静默地新增重载,而不是覆盖。结果就是多态失效,而且毫无提示。
立即学习“C++免费学习笔记(深入)”;
- 每次重写虚函数,都必须显式加上
override,否则等于放弃类型安全校验 - 如果父类虚函数是
virtual void f(int&),子类写成void f(const int&)并加override,编译直接失败 - VS 和 GCC 都支持
-Woverloaded-virtual等警告,但override是唯一能堵住这类错误的硬性手段
虚函数机制本身不复杂,但它的行为高度依赖对象生命周期阶段和编译器插入的隐式操作。最容易漏掉的,就是构造/析构中虚调用的实际目标,以及 override 缺失导致的静默重载——这两处一出错,调试时往往要花几倍时间倒推。








