关于这个问题,国内外都有许多争论,但我至今未找到满意的答案,至于为为什么,这就是今天要说明的。
可能大家都有看过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 initialization2 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++初学者,有错误请指出。
直接初始化和复制初始化
最新推荐文章于 2024-10-04 17:07:53 发布