我想尝试一下我读到的关于在C ++中按值返回的内容(它与在新对象中创建时传递值相同)我有这样的代码:
#include <iostream>
using namespace std;
class Kar{
public:
int n;
static int no;
Kar(){
n = ++Kar::no;
cout << "Creating Kar " << n << endl;
}
Kar(Kar &k){
n = ++Kar::no;
cout << "Copying Kar " <<k.n<< " to new Kar " << n << endl;
}
~Kar(){
cout << "Destroying Kar "<< n << endl;
}
Kar& operator= (const Kar &k);
};
Kar& Kar::operator= (const Kar &k){
cout << "Assigning Kar "<< k.n <<" to Kar "<< this->n << endl;
return *this;
}
int Kar::no;
Kar foo(){
cout << "Starting foo()" << endl;
Kar k;
cout << "Finishing foo()" << endl;
return k;
}
int main(int argc, char **argv) {
cout << "Starting!" << endl;
Kar k;
k=foo();
// Kar k2 = foo();
cout << "Finishing!" << endl;
return 0;
}
终端输出是:
Starting!
Creating Kar 1
Starting foo()
Creating Kar 2
Finishing foo()
Assigning Kar 2 to Kar 1
Destroying Kar 2
Finishing!
Destroying Kar 1
我希望foo()
中的行为为: a。创建Kar 2 b。将其复制到Kar 3并返回(随后将Kar 3分配给Kar 1)。为什么不呢?
如果我取消注释Kar k2 = foo();
,我会收到编译器消息:
错误:没有用于调用Kar :: Kar(Kar)的匹配函数
当然,我无法添加构造函数Kar(Kar k){ }
,因为它无效。这是什么意思?为什么构造函数Kar(Kar &k)
不用于这种情况?
答案 0 :(得分:5)
您看到的行为称为return value optimization。因此,代替编译器为返回值创建临时对象,它将消除它,并使用在return语句之后已经创建的对象。
关于你的第二个问题,你得到一个编译器错误,因为你不能将非常量临时值(r值)绑定到l值引用(BTW MSVC接受此但它是非标准行为)。尝试将复制构造函数更改为:
Kar(const Kar& kar);
答案 1 :(得分:3)
编译器最有可能使用名为copy elision的优化,它用于尽可能避免不必要的对象复制。按值返回对象时这很常见。在您的情况下,foo()
不需要保留其Kar
的实例,因为它在return
语句之后立即销毁;在main()
返回之后,Kar()
才会对foo()
的实例进行操作。因此,为两者使用一个实例是安全的。
您可以在此问题上阅读有关它的更多信息:What are copy elision and return value optimization?
编辑:一开始没有发现你的第二个问题。复制构造函数应始终采用const
引用参数,即const Kar &
,否则最终会尝试对r值进行非const引用,这是不允许的。