You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

复制含虚拟基类的对象致shared_ptr持对象丢失,程序是否合法?

菱形虚拟继承下shared_ptr输出0的原因及行为判定

代码重现

#include <iostream>
#include <memory>

struct A {
    std::shared_ptr<int> u{ new int };
};

struct B : virtual A {};
struct C : virtual A {};
struct D : B, C {};

int main() {
    D d;
    d = D( d );
    std::cout << d.u;
}

核心问题拆解

1. std::cout << d.u的输出含义

C++标准库并未为std::shared_ptr重载operator<<,编译器会尝试隐式转换:

  • C++11及之后,std::shared_ptr的operator bool()是显式的,理论上该语句会编译失败;但部分旧编译器或非标准实现允许隐式转换为bool,此时输出0表示d.u为空(get() == nullptr)。
  • 若编译器将其解释为输出指针地址,0直接表示d.u指向空。

2. d = D(d)的行为差异(依赖C++版本)

C++17及以后

C++17明确规定赋值运算符的右侧表达式先于左侧求值,因此流程是确定的:

  • 先构造临时对象D(d):虚拟基类A从d的A子对象拷贝构造,shared_ptr引用计数从1变为2。
  • 默认生成的D赋值运算符会对虚拟基类A执行一次赋值(标准强制要求),临时对象的u拷贝给d的u,引用计数变为3。
  • 临时对象销毁后,u的引用计数降至2,d.u仍有效,正常情况下输出不应为0。

C++17之前

C++17未规定赋值运算符左右操作数的求值顺序,因此d = D(d)属于未定义行为:

  • 编译器可能先修改d(赋值操作),再读取d构造临时对象,导致拷贝构造的是已被破坏的d,最终造成d.u为空,输出0只是未定义行为的一种可能结果。

结论

  • 在C++17及以后,程序不存在未定义行为,标准也不要求输出0(正常运行时d.u应有效,输出非0值)。
  • 在C++17之前,d = D(d)属于未定义行为,输出0是未定义行为的表现之一,并非标准强制要求。

内容的提问来源于stack exchange,提问作者Fedor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 10:44:52