直接初始化和复制初始化

关于这个问题,国内外都有许多争论,但我至今未找到满意的答案,至于为为什么,这就是今天要说明的。

可能大家都有看过C++ primer,我看的是第四版,中文版,英文水平不怎么样。

其中第13章,描述说,复制构造函数可用于:

1.根据另一个同类型的对象显式或隐式初始化一个对象;

2.复制一个对象,将它作为参数传递给一个函数;

3.从函数返回时复制一个对象;

4.初始化顺序容器中的元素;

5.根据元素初始化列表初始化数组元素;


关于第1点,当用于类类型对象时,直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定的构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制正在创建的对象:

string null_book = "9-999-99999-9";	//copy-initialization
sttring null_book("9-999-99999-9");    //direct-initialization

本来深信不疑的,但经过测试。问题出来了。如测试程序:

#include <iostream>
#include <vector>
class A
{
private:
	int value_;
public:
	A(int value = 0):value_(value){std::cout << "structure" << std::endl;}
	A(const A &a):value_(a.value_) {std::cout << "copy structure" << std::endl;}
};




void test(A a)
{
	
}


A test()
{
	return 4;
}


int main()
{
	int value;
	A a = 1;
	A b(2);
	test(3);
	test();
	A d[] = {1 , 2};
	std::cin >> value;
}


输出结果为:6个structure,很意外。

用VC2010,VC6,GCC测试结果均一致,跟C++ Primer所说不一致。再仔细看。

但C++ Primer 还有一句,当情况许可时,可以允许编译器跳过复制构造函数直接创建对象,但编译器没有义务这样做。

这什么意思啊!

就是A a = 1; 是否执行是要看编译器的啊,那上述的编译器中均跳过复制构造函数。是否C++ Primer有错误。


继续下去,首先http://en.cppreference.com/w/cpp/language/copy_initialization ,这个网页,说了copy_initialization ,并有GCC和clang编译器,执行上面代码结果也是一致,英文不怎样所以看不太懂。


然后,又去看了http://stackoverflow.com/questions/1051379/is-there-a-difference-in-c-between-copy-initialization-and-direct-initializati 这上面第一个答案说的跟C++ Primer基本一样。

说直接初始化是显式调用匹配的构造函数。

而复制初始化是寻找一个隐式的转换,转换成对象,(然后可能复制转换后对象到被初始化的对象,所以有可能copy构造函数是必须的,但下面这种情况不是。)

struct B;
struct A { 
  operator B();
};

struct B { 
  B() { }
  B(A const&) { std::cout << "<direct> "; }
};

A::operator B() { std::cout << "<copy> "; return B(); }

int main() { 
  A a;
  B b1(a);  // 1)
  B b2 = a; // 2)
}
// output: <direct> <copy>

GCC和clang编译器输出如代码所示,但VS2010结果却是<direct>,<direct>。

然后又去看了http://www.cplusplus.com/forum/beginner/81679/ , 这个guestgulkan 和我一样迷茫,想找到一个没有跳过复制构造的例子。

然后又去看了http://blog.csdn.net/ljianhui/article/details/9245661


c++ primer 还有一段这样说
“通常直接初始化和复制初始化仅在低级别优化上存在差异,然而,对于不支持复制的类型,或者使用非explicit构造函数的时候,它们有本质区别:
1 ifstream file1("filename")://ok:direct initialization
2 ifstream file2 = "filename";//error:copy constructor is private
在VS2010中也编译不过,但编译不过的原因并不是copy constructor is private,而是构造函数是显式的,即加了explicit声明。在VS2010中 copy constructor是私有的或有显示调用声明的,也能编译过。


综合权威观点,也就是说复制初始化,不一定会调用copy构造函数。 但是就像c++还没有规定int是占几字节,只定义了最小为16位,但现在流行的编译都通常认为是4字节。所以必须得有个靠谱的结论。


不搞清这个问题,自己写的代码,都不知到底是怎样的效果。除非只使用

A a; 

A b = a;

 即类型相同复制初始化,这样是只执行复制初始化的。不搞清这个问题,就不要使用不同类型的复制初始化了。


总后结论。


所以,在cl编译器,可以认为复制初始化就是隐式的直接初始化。

A(int value = 0):value_(value){std::cout << "structure" << std::endl;}

不变为explicit A(int value = 0):value_(value){std::cout << "structure" << std::endl;} 的情况下。

A a = 1 和 A a(1)类似的复制初始化和直接初始化没有区别的。


而在GCC/clang 编译器上,除了 stackoverflow 所说的那个有类型操作符转换这种情况的之外,还有个不同就是:

GCC/clang明明做了这个义务,即跳过了执行复制构造,但在编译的时候,还会检查其是否必须显示调用和是否公开方法。

即把 A(const A &a):value_(a.value_) {std::cout << "copy structure" << std::endl;}

变为explicit A(const A &a):value_(a.value_) {std::cout << "copy structure" << std::endl;} 或 把这个方法变为非公开,则会产生编译错误。

除了这两点 和 cl编译器的那点,A a=1 和 A a(1)类似的复制初始化和直接初始化没有区别的。


只是个c++初学者,有错误请指出。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值