对象切片发生在用基类对象值传递、赋值或容器存储派生类对象时,导致派生部分数据丢失、虚函数表被替换,多态失效;避免方法是始终使用引用或指针,禁用基类的拷贝构造与赋值操作。

对象切片(Object Slicing)是 C++ 中因值传递(而非引用或指针)导致派生类对象被“截断”为基类对象,从而丢失派生部分数据的现象。它常在多态场景下被误用,表面看似正常编译运行,实则悄悄丢掉关键信息。
什么情况下会发生对象切片?
当用基类对象接收派生类对象(尤其是通过值传递、赋值或容器存储时),编译器会自动调用基类的拷贝构造函数或赋值运算符,只复制基类子对象,忽略派生类新增的成员变量和虚函数表细节。
- 函数参数是基类 对象(非引用/非指针):
void func(Base b) { ... }→ 调用Base::Base(const Base&),派生部分被丢弃 - 用基类对象直接赋值派生类对象:
Base b = Derived(); - 把派生类对象存入
std::vector等基类值容器中
为什么多态在这里“失效”了?
多态依赖虚函数表指针(vptr)和动态绑定,而切片后只剩基类子对象——vptr 指向基类虚表,所有虚函数调用都静态绑定到基类实现,即使原对象是派生类,也完全无法体现多态行为。
更隐蔽的是:如果派生类有额外数据成员(如 string name、int id),这些字段在切片后彻底消失,不会报错也不会警告,但逻辑已出错。
立即学习“C++免费学习笔记(深入)”;
如何避免对象切片?
核心原则:**让多态对象始终以指针或引用方式参与操作,禁止按值传递或存储**。
- 函数参数改用
const Base&或Base*:保留原始对象身份和虚函数机制 - 容器改用智能指针:
std::vector<:unique_ptr>>或std::vector<:shared_ptr>> - 禁止编写接受基类值参数的多态接口;若必须值语义,应显式设计为深拷贝或禁用派生类隐式转换
- 可考虑将基类的拷贝构造函数和赋值运算符声明为
= delete,强制使用者意识到切片风险
一个典型错误例子
假设 Animal 是基类,Dog 继承它并添加 string breed:
Dog d{"Golden Retriever"};
Animal a = d; // 切片发生:a 中没有 breed 字段,且调用 a.speak() 永远是 Animal::speak()
后续任何依赖 breed 的逻辑都会出错,而编译器不提示。
基本上就这些。切片不是语法错误,而是语义陷阱——不复杂但容易忽略。









