C++-这个程序中注释中标注的三行语句会调用拷贝构造函数吗? 为什么?
RT
#include <iostream>
using namespace std;
class Base
{
public:
Base(int value):i(value){
cout << "In Base, ctorn";
}
Base(const Base& rBase){
cout << "In Base, copy ctorn";
}
~Base(){
cout << "In Base, dtorn";
}
private:
int i;
string str;
};
Base foo()
{
Base base(1024);
return base;
}
int main()
{
Base base0(1024);
Base base1 = Base(1024); // 第一行语句
Base base2 = (Base)1024; // 第二行语句
Base base3 = foo(); // 第三行语句
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
其实最有可能调用的是foo()那句,但对于直接返回局部Base变量的函数,编译器这时会直接将base3指向局部变量,你可以打印下地址,两者是一样的。但如果返回是Base&,这时编译会有警告,这时候就相当于把一个其他的Base对象赋给base3了,因此这时会调用拷贝构造函数。
我在本地运行后,发现确实任何一句都没有调用copy构造函数,可是书上写的应该是会调用的,看图:
我不知道为什么,求高手解答
VS2008编译结果是只有第三行调用了拷贝构造函数。
第一行会调用构造函数,生成一个临时对象,然后调用拷贝赋值操作符,赋值给base1.(刚又写了一个程序测试了一下,貌似没有调用拷贝赋值操作符,应该是被编译器优化掉了)。具体看后面的代码。
第二行会调用构造函数。具体原因还不是很清楚,再查查资料~
第三个会调用构造函数和拷贝构造函数。在函数foo里面第一行显然会调用构造函数。之后按值返回,生成对象base3,调用拷贝构造函数。这个没什么问题,具体参考《C++ Primer》即可。
测试代码(VS2008):
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
i = 0;
cout << "implicit default constructor" << endl;
}
explicit Base(int value):i(value){
cout << "In Base, ctorn";
}
Base(const Base& rBase){
cout << "In Base, copy ctorn";
}
//定义拷贝赋值操作符
Base& operator=(const Base& rhs)
{
this->i = rhs.i;
this->str = rhs.str;
cout << "copy assignment operator" << endl;
return *this;
}
~Base(){
cout << "In Base, dtorn";
}
int getI()
{
return i;
}
private:
int i;
string str;
};
Base foo()
{
Base base(1024);
return base;
}
int main()
{
Base base0(1024);
//cout << "base0---i : " << base0.getI() << endl;
Base base1 = Base(1024); // 第一行语句
//cout << "base1---i : " << base1.getI() << endl;
Base base2 = (Base)1024; // 第二行语句
//cout << "base2---i : " << base2.getI() << endl;
Base base3 = foo(); // 第三行语句
//cout << "base3---i : " << base3.getI() << endl;
Base base4;
base4 = base0;
return 0;
}
运行结果:
这个跟编译器优化有关,我用两个编译器测试了一下:
1.VS2008
2.GCC
对于一个如foo()这样的函数,如果它的每一个返回分支都返回相同的对象,编译器有可能对其做Named return Value优化(简称NRV优化),方法是以一个参数result取代返回对象。
foo()的原型:
X foo()
{
X xx;
if(...)
returnxx;
else
returnxx;
}
优化后的foo(),以result取代xx:
void foo(X &result)
{
result.X::X();
if(...)
{
//直接处理result
return;
}
else
{
//直接处理result
return;
}
}
对比优化前与优化后的代码可以看出,对于一句类似于X a=foo()这样的代码,NRV优化后的代码相较于原代码节省了一个临时对象的空间(省略了xx),同时减少了两次函数调用(减少xx对象的默认构造函数和析构函数,以及一次拷贝构造函数的调用,增加了一次对a的默认构造函数的调用)。
注:关于NRV的编译器优化机制和原理可详细参考《深入探索C++对象模型》第二章 拷贝构造函数的语义学
在C++中,下面三种对象需要调用拷贝构造函数(有时也称“复制构造函数”):
1) 一个对象作为函数参数,以值传递的方式传入函数体;
2) 一个对象作为函数返回值,以值传递的方式从函数返回;
3) 一个对象用于给另外一个对象进行初始化(常称为复制初始化);
从运行结果上来看,只有
Base base3 = foo(); // 第三行语句
调用了拷贝构造函数,这个是没有问题,因为是按值返回(return by value),必然会调用拷贝构造函数。(补充:这句话在VS2008,debug模式下编译成立,但在release模式下是不成立的。gcc4.4下也不成立)
倒是第一,二行语句的情况不好判断,我觉得还是要归结为“聪明的”的编译器做了优化工作。
看看汇编代码,main函数前3个语句生成的汇编代码并没有区别(VS2008,debug)
; 32 : Base base0(1024);
push 1024 ; 00000400H
lea ecx, DWORD PTR _base0$[ebp]
call ??0Base@@QAE@H@Z ; Base::Base
mov DWORD PTR __$EHRec$[ebp+8], 0
; 33 : Base base1 = Base(1024); // 第一行语句
push 1024 ; 00000400H
lea ecx, DWORD PTR _base1$[ebp]
call ??0Base@@QAE@H@Z ; Base::Base
mov BYTE PTR __$EHRec$[ebp+8], 1
; 34 : Base base2 = (Base)1024; // 第二行语句
push 1024 ; 00000400H
lea ecx, DWORD PTR _base2$[ebp]
call ??0Base@@QAE@H@Z ; Base::Base
mov BYTE PTR __$EHRec$[ebp+8], 2
拓展一下,对于如下代码:
Base foooo()
{
return Base(2048);
}
在main函数这样调用:
Base base4 = foooo();
编译器对于这种情况会做返回值优化,只需要调用构造函数,比起foo()函数中按值返回局部对象的方式,foooo()函数效率要高,因为它不需要调用额外的拷贝构造函数和析构函数。