必须用指针或引用实现多态,否则值传递会触发切片,丢失子类成员和虚函数表,导致多态失效;只有base*或base&才能保留动态类型信息,通过vtable调用正确虚函数。

为什么必须用指针(或引用)才能实现多态
直接用对象值传递会触发“切片(slicing)”,子类特有成员和虚函数表信息全丢,只剩基类部分。多态失效不是语法报错,而是运行时调用基类版本,且毫无提示。
例如:Base b = Derived(); 这行代码合法,但 b.func() 永远调用 Base::func(),哪怕它被声明为 virtual。
只有通过 Base* 或 Base& 才能保留完整的动态类型信息,让虚函数调用走 vtable 查找。
虚函数 + 指针调用的最小可行写法
基类必须至少有一个 virtual 函数(通常建议把析构函数也设为 virtual),子类用 override 显式重写。指针本身类型是基类,但指向子类对象。
<pre class="brush:php;toolbar:false;">class Base {
public:
virtual ~Base() = default; // 关键:避免 delete ptr 时未定义行为
virtual void say() { std::cout class Derived : public Base {
public:
void say() override { std::cout <p>int main() {
Base<em> p = new Derived(); // 指针类型是 Base</em>,实际指向 Derived 对象
p->say(); // 输出 "Derived" —— 多态生效
delete p; // 安全:因 Base::~Base() 是 virtual
}</p>常见踩坑:忘记 virtual 析构函数
如果 Base::~Base()
virtual,而你用 Base* 指向 Derived 对象并 delete,只会调用 Base 的析构函数,Derived 的析构逻辑(如释放资源、关闭文件等)完全跳过 —— 典型内存泄漏或资源泄漏。这不是编译错误,运行时也未必崩溃,但行为不可靠。
-
std::unique_ptr<base>同样要求Base有virtual析构,否则~unique_ptr里 delete 仍不安全 - 纯虚析构函数写法:
virtual ~Base() = 0;+ 在类外定义Base::~Base() {} - 若基类只用于接口、不实例化,可直接用
= 0;若允许实例化,则不能纯虚
指针 vs 引用:选哪个更合适
两者都能支持多态,但语义和使用约束不同:
- 指针可为空(
nullptr),适合表示“可能无对象”的场景,比如工厂函数返回Base*;但每次调用前需判空 - 引用必须绑定有效对象,天然避免空悬风险,适合函数参数传入(如
void process(const Base& obj)),语义更清晰 - 引用无法重新绑定,指针可以重指向;但多态本身不依赖重绑定能力
- 性能无差异:底层都是地址传递,vtable 查找开销相同
实践中,优先用引用传参;需要延迟绑定、动态创建/销毁或可能为空时,才用指针。
虚函数机制本身不关心你是用指针还是引用,只关心“是否通过基类视图访问派生对象”。只要绕过值语义,就守住了多态的门。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











