C++-这个程序中注释中标注的三行语句会调用拷贝构造函数吗? 为什么?

发布于 2016-11-04 20:30:19 字数 680 浏览 1354 评论 6

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

夜无邪 2017-09-01 07:47:25

其实最有可能调用的是foo()那句,但对于直接返回局部Base变量的函数,编译器这时会直接将base3指向局部变量,你可以打印下地址,两者是一样的。但如果返回是Base&,这时编译会有警告,这时候就相当于把一个其他的Base对象赋给base3了,因此这时会调用拷贝构造函数。

甜柠檬 2017-07-25 22:53:11

我感觉都会吧,因为base1,base2,base3的写法都是定义式,所以它不会调用赋值构造函数,因为代码执行时,它们还不存在

我在本地运行后,发现确实任何一句都没有调用copy构造函数,可是书上写的应该是会调用的,看图:

我不知道为什么,求高手解答

虐人心 2017-05-30 07:50:28

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;
}

运行结果:

瑾兮 2017-03-02 12:26:19

这个跟编译器优化有关,我用两个编译器测试了一下:

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++对象模型》第二章 拷贝构造函数的语义学

甜柠檬 2017-02-13 06:41:06

 在C++中,下面三种对象需要调用拷贝构造函数(有时也称“复制构造函数”):  

 1) 一个对象作为函数参数,以值传递的方式传入函数体;
 2) 一个对象作为函数返回值,以值传递的方式从函数返回;
 3) 一个对象用于给另外一个对象进行初始化(常称为复制初始化);

晚风撩人 2016-11-29 18:08:58

从运行结果上来看,只有

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()函数效率要高,因为它不需要调用额外的拷贝构造函数和析构函数。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文