对象切片是c++值语义下的确定行为:将派生类对象赋值给基类值对象时,派生部分被静默丢弃,仅保留基类子对象,虚函数表重置,dynamic_cast失败,且不可恢复。

对象切片不是 bug,而是 C++ 值语义下的确定行为:当你把一个 Dog 对象赋值给 Animal 变量时,Dog 里独有的成员(比如 tricks、favorite_toy)和重写的虚函数实现,会**被静默丢弃**,只剩下一个干净但“残缺”的 Animal 对象。
对象切片发生在哪些赋值场景
只要目标是基类的「值对象」(非指针、非引用),就切片。编译器不会报错,但数据和行为已经不可逆地丢失。
-
Base b = derived;—— 拷贝构造时切片 -
b = derived;—— 赋值运算符调用时切片 -
std::vector<base> v; v.push_back(derived);—— 容器存值,每 push 一次都切一次
注意:Base& b = derived; 或 Base* p = &derived; 不切片——它们不创建新对象,只是访问原对象。
按值传参是高频踩坑点
函数声明写成 void handle(Base b),哪怕你传进去的是 Dog 或 Cat,进函数那一刻就变成纯 Base 了。虚函数调用走的是 Base::speak(),派生类的逻辑彻底失效。
- 错误写法:
void feed(Animal a) { a.speak(); }→ 总是输出基类行为 - 正确写法:
void feed(const Animal& a) { a.speak(); }→ 多态生效 - 如果必须修改,用
Animal&;若不确定生命周期,用std::unique_ptr<animal></animal>或std::shared_ptr<animal></animal>
切片后虚函数表也被重置
切片不是“暂时屏蔽”派生类行为,而是彻底重建了一个基类对象。它的 vptr 指向 Base 的虚函数表,所有虚函数调用都绑定到基类实现。
- 即使原
Dog重写了speak(),切片后的Animal对象调用speak()也绝不会执行Dog::speak() -
dynamic_cast在切片对象上必然失败:dynamic_cast<dog>(&a)</dog>返回nullptr - 没有运行时机制能“恢复”被切掉的成员——内存里根本不存在它们
最易被忽略的是容器场景:std::vector<animal></animal> 看似方便,实则强制抹平所有派生差异。真要存多态对象,得用 std::vector<:unique_ptr>></:unique_ptr>,否则等于主动放弃继承设计的全部价值。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











