对象切片指派生类对象赋值/传递给非引用非指针的基类对象时,丢失派生类特有成员和虚表指针,导致多态失效;典型场景包括直接赋值、值传递参数、容器存基类对象。

对象切片(Object Slicing)是指在 C++ 中,当一个派生类对象被赋值或传递给基类对象(非引用、非指针)时,派生类中独有的成员和行为被“截掉”,只保留基类部分的数据,导致信息丢失的现象。这是多态编程中最容易踩的坑之一,表面看代码能编译运行,实际却悄悄破坏了设计意图。
切片发生的典型场景
切片不是语法错误,而是语义陷阱,常见于以下几种写法:
- 用基类对象接收派生类对象:Base b = Derived();
- 函数参数按值传递基类:void func(Base b) { ... }; func(Derived());
- 容器存储基类对象(如 std::vector
)并插入派生类实例
为什么切片后多态失效?
多态依赖虚函数表(vtable)和动态绑定,但切片后得到的是一个纯基类对象——它有自己的独立内存布局,不包含派生类字段,也没有派生类的 vtable 指针。调用虚函数时,只会绑定到基类版本,哪怕原对象是派生类实例,也完全无法触发重写逻辑。
例如:
立即学习“C++免费学习笔记(深入)”;
class Animal { public: virtual void speak() { cout << "Animal"; } };
class Dog : public Animal { public: void speak() override { cout << "Woof!"; } int tail_wag_count = 5; };
Dog d;
Animal a = d; // 切片发生:tail_wag_count 丢失,speak() 调用 Animal 版本
a.speak(); // 输出 "Animal",不是 "Woof!"
如何避免切片?关键就三点
- 优先使用引用或指针:传参、返回、存储都用 Animal& 或 Animal*,保持对象身份不变
-
容器存智能指针:用 std::vector<:unique_ptr>> 替代 std::vector
- 禁用基类的拷贝/赋值(可选防御):在基类中将拷贝构造函数和拷贝赋值运算符声明为 delete,强制使用者面对问题
切片 vs. 向上转型(upcast)的区别
向上转型(如 Animal* p = &d;)是安全的,只是改变视角,对象本体没变;切片是真实发生了对象复制和数据丢失。区别核心在于:是否涉及对象的值拷贝。有等号、函数传值、容器直接存对象——大概率已切片。
基本上就这些。切片不复杂但容易忽略,多看一眼赋值右边是不是派生类、左边是不是非引用非指针的基类,就能避开大半坑。











