std::shared_ptr<A>赋值子类B对象是否安全?是否存在内存溢出风险?
这种操作并不安全(但不是因为你担心的内存溢出)
首先直接给结论:你这段代码不会触发内存溢出,但它是一种不符合预期的错误用法,而且C标准库完全不会为这种场景做“妥善处理”——因为这是C对象模型的基本规则决定的。
1. 你的代码到底在做什么?
先拆解关键两行代码的行为:
std::shared_ptr<A> pa = std::make_shared<A>(); *pa = B();
- 第一行:
make_shared<A>()在堆上分配了一块恰好能容纳A类型对象的内存,创建一个A实例后交给pa管理,这块内存的大小固定为sizeof(A)。 - 第二行:
B()生成一个临时B对象,随后执行A::operator=(const A&)(编译器自动生成的默认赋值运算符)。这里会发生对象切片:临时B对象中只有继承自A的部分会被保留,然后复制到pa指向的A对象里。
简单说,B对象里的arr[1000]根本不会被触及,赋值操作只修改了A对象的成员(你的例子里A没有成员,所以实际什么都没做)。不存在“把B的大内存塞进A的小内存”的情况,自然不会有内存溢出。
2. 但为什么说这种操作不安全?
问题在于你可能误以为这段代码会让pa指向的对象变成B类型,但实际上:
- pa指向的对象始终是A类型,它的内存大小、虚函数表(如果A有虚函数)都不会改变。
- 如果A有虚函数,后续通过pa调用虚函数时,依然会执行A的版本,而非B的,这会导致完全不符合预期的行为。
- B对象的所有额外状态都被丢失了,相当于白创建了这个临时B对象。
3. 正确的做法是什么?
如果你想让shared_ptr<A>指向一个B类型的对象,应该直接创建B实例:
// 直接创建B对象,pa管理的是完整的B类型内存 std::shared_ptr<A> pa = std::make_shared<B>();
如果已经有了一个pa,想换成B对象,直接重新赋值即可:
pa = std::make_shared<B>();
这时候原来的A对象会被shared_ptr自动销毁,pa转而管理一个新的B对象,内存大小是sizeof(B),后续也能安全地进行多态操作(只要A有虚函数)。
总结
C标准库不会干预这种切片赋值的行为——这是C静态类型系统的一部分:赋值运算符不会改变对象的实际类型,只会修改对象的成员值。你的代码不会溢出,但它是一种错误的用法,无法实现“让pa指向B对象”的目的。
内容的提问来源于stack exchange,提问作者Jackson
相关产品推荐
相关产品推荐

