C++直接初始化和复制初始化的疑问

winux1062 2012-11-17 07:53:35
C++primer上说:
“直接初始化和复制初始化。复制初始化使用=符号,而直接初始化将初始化式放在圆括号中。
当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时对象(7.3.2节),然后使用复制构造函数将那个临时对象复制到正在创建的对象:
string null_book = "9-999-99999-9 ";//copy-initialization
string dots(10, '. ');//direct-initialization
string empty_copy = string();//copy-initialization
string empty_direct;//direct-initialization
对于类类型对象,只有指定单个实参或显式创建一个临时对象用于复制时,才使用复制初始化。
创建dots时,调用参数为一个数量和一个字符的string构造函数并直接初始化dots的成员。创建null_book时,编译器首先调用接受一个C风格字符串形参的string构造函数,创建一个临时对象,然后,编译器使用string复制构造函数将null_book初始化为那个临时对象的副本。
empty_copy和empty_direct的初始化都调用默认构造函数。对前者初始化时,默认构造函数创建一个临时对象,然后复制构造函数用该对象初始化empty_copy。对后者初始化时,直接运行empty_direct的默认构造函数”
请看下面的例子:

#include <iostream>
#include <cstdlib>

using namespace std;

class A
{
public:
A(){cout << "constructor called!" << endl;}
A(int i) : x(i){cout << i << " constructor called!" << endl;}
A(const A &a){cout << x << " copy constructor called!" << endl;}
~A(){cout << x << " destructor called" << endl;}
private:
//A(const A &a){cout << "copy constuctor called!" << endl;}
int x;
};

A get_A()
{
A a(5);
return a;
}

int main()
{
A a = 1;system("pause"); // 1

A b = get_A();system("pause"); // 2

b = a;system("pause"); // 3

A c = A(2);system("pause"); // 4

b = 3;system("pause"); // 5

A d = b;system("pause"); // 6
return 0;
}

在CB中编译运行结果:

按照B.Lippman的说法, 显示的结果应该不是这个样子的, 但是编译器有优化, 跳过了有些不必要的复制构造函数的调用, 现在说说我的理解, 不知道对不对, 希望大家都说说自己的看法, 谢谢!
第1句, 生成了临时对象, 但是编译器优化,跳过了复制构造函数。
第2句, 在VS中的运行结果是这样的:

当从函数返回该类型的对象时, 调用复制构造函数, VS中是正常应该出现的结果, 但是CB中却没有, 我猜是g++优化的比VS好吧。
第3句, 调用的是编译器默认生成的赋值操作符(=), 大概应该是这样的:

void operator = (const A &a)
{
x = a.x;
}

第4句, 和第一句差不多, 我猜第1句A a = 1等效于A a = A(1).
第5句, 先生成临时对象, 然后再调用编译器生成的赋值操作符(=)。
第6句, 从结果显示只调用了复制构造函数, 到底有没有临时对象生成, 请大家说说自己的看法。
另外, 我把get_A函数换成了如下形式:

A get_A()
{
return A(5);
}

在VS中的显示结果:

我很疑问, 复制构造函数怎么没有被调用?难道又是编译器的优化? 请大家给我解释解释, 谢谢!
...全文
232 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
yisikaipu 2012-11-18
  • 打赏
  • 举报
回复
引用 2 楼 WJ_1062 的回复:
并没有消除复制构造函数的调用, 请问, 我是不是设置错了, 你是怎么设置的, 谢谢!还有就是第6种情况, 复制构造函数式在什么时候调用的, 是等价于A d = A(b)吗?
对于你这个例子,搞不懂为什么你那里没有消除拷贝构造,我手头没有2010,但在2008和2012上试过,都是消除的 我#1楼提到的局部变量和临时变量的优化区别什么的,都只是瞎猜,当不得真,换个例子就不一样(见最后) 至于第6种情况,这里为什么有疑问呢?明显就是拷贝构造,就这么简单,为何有临时变量之疑虑? 倘若增加代码如下
class B;
class A
{
public:
    A(){cout << "constructor called!" << endl;}
    A(int i) : x(i){cout << i << " constructor called!" << endl;}
    A(const A &a){cout << x << " copy constructor called!" << endl;}
	explicit A(const B &b){cout << x << " copy constructor called, via A(const B&)!" << endl;}
    ~A(){cout << x << " destructor called" << endl;}
private:
    //A(const A &a){cout << "copy constuctor called!" << endl;}
    int x;
};

class B: public A
{
};
 
A get_A()
{
    A a(5);
    return a;
}
 
int main()
{
    A a = 1;//system("pause");       // 1
	cout <<endl;
 
    A b = get_A();//system("pause"); // 2
	cout <<endl;
 
    b = a;//system("pause");         // 3
	cout <<endl;
 
    A c = A(2);//system("pause");    // 4
	cout <<endl;
 
    b = 3;//system("pause");         // 5
	cout <<endl;
 
    A d = b;//system("pause");       // 6
	cout <<endl;

	{ // 7
		B theb;
		A e=theb;		
	}

	cout <<endl;

	{ // 8
		B theb;
		A e(theb);		
	}

	cout <<endl;

	system("pause");

    return 0;
}
那么第7种情况,这个时候尚须疑虑这里是否有临时变量。因为这里最合适的拷贝构造被explicit了,需要类型转换,(与之对比的第8种情况直接初始化则不然),而细想一下可知,这种转换无须临时变量 出于好奇,我想知道下例第一种情况在你的VS2010上是否也不做返回值优化
class Cls
{
	string name;
    int m;
public:
	Cls(string s,int i):name(s),m(i)
	{
		cout <<"ctor name=" <<name <<endl;
	}

	~Cls()
	{
		cout <<"dtor name=" <<name <<endl;
	}

	Cls(const Cls& c):m(c.m)
	{
		cout <<"copy from name=" <<c.name <<" to name=" <<name <<endl;
	}

	void disp()
	{
		cout <<"name=" <<name <<" m=" <<m <<endl;
	}

	void setname(string n)
	{
		name=n;
	}
};

Cls GetCls1(string n,int i)
{
	Cls c(n,i); // 局部变量
	return c;
}

Cls GetCls2(string n,int i)
{
	return Cls(n,i); // 临时变量
}

int main()
{
	{
		Cls x=GetCls1("x_at_right",3);
		x.setname("x_at_left");
		x.disp();
	}

	cout <<endl;
	
	{
		Cls y=GetCls2("y_at_right",4);
		y.setname("y_at_left");
		y.disp();
	}

	cout <<endl;

	{
		Cls a("a_as_temp",5);
		Cls z=a;
		z.setname("z_at_left");
		z.disp();
	}

	cout <<endl;

	{
		Cls w=Cls("w_at_right",7);
		w.setname("w_at_left");
		w.disp();
	}

	cout <<endl;

	return 0;
}
winux1062 2012-11-18
  • 打赏
  • 举报
回复
引用 3 楼 taodm 的回复:
优化不是那么容易控制的。 要写出和优化无关的代码,不要试图过于试图讨论优化的细节。
说的好啊, 再想就要崩溃了, O(∩_∩)O哈
taodm 2012-11-18
  • 打赏
  • 举报
回复
优化不是那么容易控制的。 要写出和优化无关的代码,不要试图过于试图讨论优化的细节。
winux1062 2012-11-18
  • 打赏
  • 举报
回复
引用 1 楼 yisikaipu 的回复:
引用 楼主 WJ_1062 的回复:我很疑问, 复制构造函数怎么没有被调用?难道又是编译器的优化? 请大家给我解释解释, 谢谢!

这个是返回值优化

至于为什么换个写法VS又不做返回值优化,可能是VS把情况分得比较细:一个是返回局部变量,一个是返回临时变量,对后者果断做返回值优化,对前者,debug配置下,不做优化

如果选择release,而且设置速度或者……

在VS2010中,采取如下设置

并没有消除复制构造函数的调用, 请问, 我是不是设置错了, 你是怎么设置的, 谢谢!还有就是第6种情况, 复制构造函数式在什么时候调用的, 是等价于A d = A(b)吗?
wzb56 2012-11-18
  • 打赏
  • 举报
回复
单参数的构造函数相当于转换构造函数即转换函数数,从另外一种类型转换该类类型。 可以在单参数的构造函数前,通过是使用关键字explicit,限制这种隐式转换。 你的 A a = 1; 与 A a(1);是等价的,如果使用了explicit关键字,前者是编译不能通过的。 使用=的未必就是赋值操作。有可以使初始化,关键看你初始的对象,是否已经被初始化过。 拷贝构造函数,仅使用在一种情形下,就是用一个对象初始化另一个对象,包括函数的参数的值传递,和返回值的值的值传递。
yisikaipu 2012-11-17
  • 打赏
  • 举报
回复
引用 楼主 WJ_1062 的回复:
我很疑问, 复制构造函数怎么没有被调用?难道又是编译器的优化? 请大家给我解释解释, 谢谢!
这个是返回值优化 至于为什么换个写法VS又不做返回值优化,可能是VS把情况分得比较细:一个是返回局部变量,一个是返回临时变量,对后者果断做返回值优化,对前者,debug配置下,不做优化 如果选择release,而且设置速度或者大小优化,那么两种情况都会消除了拷贝构造

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧