为什么返回不复制对象?
Why return doesn't copy the object?
以下代码只打印A::A()
,而不打印A::A(const A&)
或operator=
。为什么?
struct A
{
A() { cout << "A::A()" << endl; }
A(const A& value) { cout << "A::A(const A&)" << endl; }
A& operator=(const A& newValut)
{
cout << "A::operator=" << endl;
return *this;
}
};
A foo()
{
A a; //Ok, there we have to create local object by calling A::A().
return a; //And there we need to copy it, otherwise it will be destroyed
//because it's local object. But we don't.
}
int main()
{
A aa = foo(); //Also there we need to put result to the aa
//by calling A::A(const A&), but we don't.
}
所以这个代码必须打印
A::A()
A::A(const A&)
A::A(const A&)
但事实并非如此。为什么?
我建议在没有优化的情况下,g++
下没有foo()
的内联
这被称为"返回值优化"。在这种情况下,编译器可以删除副本。
这就是在C++中返回复杂类型的方式:返回对象的位置实际上是由调用方在调用函数之前提供的,指向这个尚未初始化对象的指针作为隐藏参数传递给函数。函数使用此内存位置从返回的表达式中构造返回的对象。
因此,当返回的对象要像程序A aa = foo();
中那样直接初始化一个新对象时,它不需要将返回的值复制到堆栈上的对象。它要求函数直接在此位置创建对象。因此,最多只需要对复制构造函数进行一次调用。(事实上,如果对复制构造函数有两个调用,C++编译器将不兼容)。
现在,最重要的是,编译器可以在一个名为"返回值优化"或RVO的优化中优化掉这个调用。这怎么可能?如果您查看代码,您可以看到您可以在foo()中的返回值的建议位置直接定义本地"a"变量,因此不必再次复制它。这是一个重要的功能,因为复制构造函数可能很复杂,运行速度也很慢,所以在实现时可以显著提高性能(我认识的每个编译器都实现了这个功能)。
因此,在您的情况下,根据编译器的不同,您可能对复制构造函数有1或0调用,并且您的编译器仍然是兼容的。
在Visual Studio 2022 17.4及更高版本中,您可以使用/Zc:nrvo编译器选项显式启用可选的复制或移动省略行为。
https://learn.microsoft.com/en-us/cpp/build/reference/zc-nrvo?view=msvc-170
- 为什么C++在将一个对象复制到另一个对象时需要对这两个对象进行低级常量限定
- 如何在向量列表初始化时避免对象复制以及如何延长临时的生存期
- 了解在 C++ 中分配容器时的对象复制
- C++类对象复制构造函数和运算符=
- 复制 elision/RVO 会导致从同一对象复制/移动吗?
- 如何在循环内将大对象复制到omp任务中?
- 为什么当我添加一个不同的对象(复制构造函数中的参数)时调用复制构造函数?
- 无法将类构造函数中新创建的对象复制到 C++ 中的向量成员
- 将C++对象复制到指向新指针的指针处
- 将一个 OpenCV OutputArrayOfArrays 对象复制到另一个对象
- 选择性地禁用隐式对象复制
- 不能从文件到指针向量的对象复制
- 从派生对象复制构造
- OpenCV Mat对象复制速度更快
- 将子对象复制到父对象
- 为什么普通的 c++ 编译器不优化对象复制?
- C :将一个对象复制到构造函数中的另一个对象
- 使用命名对象复制构造函数
- 不必要的对象复制 - C++ STL
- 如何将一个类的对象复制到另一个类中