public继承下派生类对象内存布局以基类子对象开头,支持base* ptr = &d的隐式转换;protected/private继承则禁止该转换;虚函数通过vftable实现运行时多态,析构函数须声明virtual以防资源泄漏。

因为派生类对象内存布局中包含一个完整的基类子对象,且 public 继承建立了合法的 “is-a” 关系。这不是编译器的特例处理,而是 C++ 对象模型和继承语义共同决定的底层事实。
必须是 public 继承才能成立
只有 class Derived : public Base 这种声明方式下,编译器才允许隐式转换。如果写成 protected 或 private 继承,Base* ptr = &d 会直接报错:类型不可转换。
- public 继承表示 “Derived is-a Base”,语义上合理,编译器放行
- protected/private 继承隐藏了这种关系,基类接口对外不可见,指针赋值被禁止
- 即使函数体完全一样,继承方式不同,结果天壤之别
内存布局保证首地址对齐
派生类对象的内存开头就是它的基类子对象。也就是说,&d(取派生类对象地址)和 static_cast<base>(&d) 得到的是同一块地址值。
- 这使得
Base* ptr = &d不需要移动指针、不需调整偏移,天然安全 - 但反过来
Derived* pd = &b(b 是Base对象)就危险:pd 会把后面未定义的内存当作Derived成员去读写 - 结构体嵌套式布局是这一机制的物理基础,不是靠运行时检查兜底
虚函数表支持多态调用
仅当基类有虚函数(比如声明了 virtual ~Base() 或其他 virtual 函数),指针才能在运行时正确绑定到派生类实现。
- 没有虚函数时,
ptr->func()总调基类版本(静态绑定) - 有虚函数时,
ptr实际查的是d对象的 vftable,自动跳转到Derived::func() - 注意:析构函数不加
virtual,用delete ptr会只调Base::~Base(),漏掉Derived部分——这是最常踩的坑
真正容易被忽略的是:这个机制不依赖 RTTI,也不需要 dynamic_cast;它在编译期就确认合法性,在运行期靠 vftable 和固定偏移工作。一旦继承方式或虚函数设计出错,问题往往延迟到 delete 或多态调用时才暴露。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











