对象切片与多态陷阱
更新时间:2026-08-27。本文是
languages/cpp/主题入门层第 31 篇,继承与多态子族收尾。按值传递派生类对象给基类参数,会发生"对象切片":派生类部分被切掉,多态悄然失效。这是 C++ 多态最隐蔽的坑,本篇讲清成因、症状和防范。
本文要回答的问题
- 对象切片是什么?什么时候发生?
- 为什么切片会让多态失效?
- 怎么避免切片?
一、切片:按值传参时的"切割"
class Shape {
public:
virtual double area() const { return 0; }
};
class Circle : public Shape {
double r_;
public:
Circle(double r) : r_(r) {}
double area() const override { return 3.14159 * r_ * r_; }
};
void show(Shape s) { // 注意:按值传
std::cout << s.area() << "\n";
}
Circle c(5.0);
show(c); // 输出 0!不是预期的 78.5show(c) 按值传给 Shape s——c 被复制成 Shape,复制过程只拷贝基类部分,Circle 独有的 r_ 被切掉,虚表指针也指向 Shape 的虚表。于是 s.area() 调的是 Shape::area,输出 0。这个过程就叫对象切片(slicing)。

二、为什么会有切片
根源是按值传参时对象的大小是固定的:Shape s 在栈上分配的是 Shape 大小的空间,装不下 Circle。复制时只能"切一块装得下的"——基类部分。C++ 语义上,"Circle 是 Shape 的一种"允许隐式转换,但按值转换必然切割。
同样的坑还有:
std::vector<Shape> shapes; // 容器装的是 Shape(按值)
shapes.push_back(Circle(5.0)); // 切片!Circle 变成 Shape 存进去std::vector<Shape> 里每个元素都是固定大小的 Shape,push 进 Circle 就是切片,再取出来 area() 也全是 0。
三、防范:传引用或指针
多态的前提是对象本身不复制。参数用引用/指针,只传地址,不切割:
void show(const Shape& s) { // 引用:不拷贝
std::cout << s.area() << "\n"; // 78.5,多态正常
}
void show2(const Shape* s) { // 指针同理
std::cout << s->area() << "\n";
}
show(c); // 正常
show2(&c); // 正常容器要存多态对象,得用指针或智能指针:
std::vector<std::unique_ptr<Shape>> shapes; // 存指针,不切片
shapes.push_back(std::make_unique<Circle>(5.0));四、切片还出现在哪
| 场景 | 症状 |
|---|---|
| 按值传参 | 多态失效,静默调基类版本 |
| 按值返回 | 同左 |
vector<Shape> 存派生类 | 数据全变基类部分 |
| 派生类对象直接赋值给基类变量 | Shape s = c; 同左 |
诊断口诀:只要看到"把派生类对象按值塞给基类类型",就要警惕切片。C++ 不报错(隐式转换是合法的),所以是静默坑。好在编译器有提示:-Wold-style-cast 不管这个,但工具链的 -Wextra 对部分情况有警告,跑起来更直接的信号是"多态失效但代码没毛病"。
五、和 C 对比
| 对比项 | C | C++ |
|---|---|---|
| 结构体切片 | 无继承概念 | 派生类按值转基类会切 |
| 传参方式 | 传指针是常规 | 引用/指针是防切片的关键 |
| 容器存多态 | 指针数组 | 指针/智能指针容器 |
C 没有继承,谈不上切片。但"按值传结构体会复制"这件事两门语言一样——只是 C++ 里复制基类部分恰好产生"切片"这种反直觉行为。
六、与本站主线衔接
- 切片与多态、虚表的联动,回看 虚函数与多态;
std::unique_ptr容器是存多态对象的标准姿势,见 RAII 与智能指针;- 下一篇进入 STL:
std::vector用法与容量(存量stl-containers拆解迁入)。
一句话总结
把派生类对象按值传给基类参数/存入基类容器,会发生对象切片:只拷贝基类部分、虚表也换成基类的,多态悄然失效且编译不报错;防范只有一条——传引用或指针(const Shape& / Shape* / std::unique_ptr<Shape> 容器),永远别按值处理多态对象。
上一篇:抽象类与纯虚函数 下一篇:vector 用法与容量