C++拷贝构造函数拷贝的函数返回值内存存储位置及相关问题
C++拷贝构造函数返回场景问题解答
你提到的拷贝构造函数两次调用的行为,是关闭编译器复制消除优化时符合C++基础语义的表现,现代编译器默认开启优化的场景下会消除冗余拷贝,以下针对你的三个问题逐一解答:
问题1:被返回的对象c被拷贝到了内存中的哪个位置?
- 无优化场景下,函数按值返回的对象会先拷贝到调用方栈帧的预留临时存储区:main作为foo的调用方,在执行foo调用前会先在自身栈空间中预留一块和
Cube类型大小匹配的临时内存,foo内执行return c时,会将foo栈帧上的局部对象c拷贝到这块main预留的临时内存中,foo栈帧销毁后局部c被回收,返回值的副本就保存在这块临时内存里。
问题2:为什么不直接将返回对象c拷贝存储到c2中?这样的实现方式效率不是更高吗?
- 你说的这种优化是实际存在的,就是C++标准明确支持的具名返回值优化(Named Return Value Optimization, NRVO),属于复制消除(copy elision)机制的一种。现代GCC、Clang、MSVC等编译器在开启优化(哪怕是最低级的-O1)时都会默认启用该优化:直接将foo内的局部对象c构造在main中c2对应的内存地址上,全程不需要调用拷贝构造函数,完全实现了你说的更高效率的逻辑。
- 未优化时会走两次拷贝的逻辑,是因为编译器需要先遵循C++的基础语义要求:按值返回的流程默认是「先构造返回值临时对象,再用临时对象初始化目标变量」的两步流程,优化属于标准允许的额外逻辑,默认未开优化时编译器会严格执行基础语义流程。
问题3:如果返回的对象c没有被存储到类似c2的变量中,为什么仍然会调用拷贝构造函数?
- 因为C的基础语义要求:只要函数是按值返回对象,且没有触发复制消除的条件,就必须构造一个完整的返回值临时对象,不管这个临时对象后续是否被使用。临时对象会在当前完整表达式执行结束后自动销毁,哪怕你没有变量接收它,临时对象的拷贝构造(或C11后的移动构造)、析构流程也会完整执行。
验证提示:使用GCC编译时添加
-fno-elide-constructors参数会关闭复制消除,就能复现你提到的两次拷贝构造调用;默认编译不加该参数时,两次拷贝都会被优化消除,全程只会调用一次Cube的默认构造函数。
内容的提问来源于stack exchange,提问作者Hammad Ali
相关产品推荐
相关产品推荐

