C++怎么继承类 C++多态性如何通过虚函数实现【精讲】

6次阅读

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

C++怎么继承类 C++多态性如何通过虚函数实现【精讲】

继承时子类怎么调用父类构造函数

子类对象创建时,父类部分必须被初始化,但编译器不会自动帮你传参——你得显式写出来。不写的话,只会在父类有默认构造函数时“侥幸通过”;一旦父类只有带参构造函数,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 缺失导致的静默重载——这两处一出错,调试时往往要花几倍时间倒推。

text=ZqhQzanResources