c++中的直接初始化与复制初始化

来源:互联网 发布:java 量化交易系统 编辑:程序博客网 时间:2024/05/22 00:51

按照C++   Primer第四版中文版13.1节中的说法:
1.对象的定义
回忆一下,C++支持两种初始化形式(2.3.3节):直接初始化和复制初始化。复制初始化使用=符号,而直接初始化将初始化式放在圆括号中。
当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时对象(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的默认构造函数。

...

支持初始化的复制形式主要是为了与C的用法兼容。当情况许可时,可以允许编译器跳过复制构造函数直接创建对象,但编译器没有义务这样做。

通常直接初始化和复制初始化仅在低级别优化上存在差异。然而,对于不支持复制的类型,或者使用非explicit构造函数(12.4.4节)的时候,它们有本质区别:

ifstream file1("filename");//ok:direct initialization

ifstream file2 = "filename";//error:copy constructor is private

//This initialization is okay only if

//the sales_item(const string&) constructor is not explicit

Sales_item item = string("9-999-99999-9");

...

item的初始化是否正确,取决于正在使用哪个版本的Sales_item类。某些版本将参数为一个string的构造函数定义为explicit。如果构造函数是显示的,则初始化失败;如果构造函数不是显示的,则初始化成功。



C/C++ code

#include   <iostream>

using   std::cout;

using std::endl; class   test

{

public:

test(int   i):ival(i){cout < < "调用了以一个int为形参的构造函数 " < <endl;}

test(const   test&   t):ival(t.ival){cout < < "调用了复制构造函数 " < <endl;}

void   operator   =   (const   test&   t)//   定义一个赋值操作符函数

{

ival   =   t.ival;

cout < < "调用了赋值操作符=函数 " < <endl;

}

private:

int   ival;

};

int   main(int   argc,char   *   argv[])

{

test   t1   =   1;   //仅调用ival(1)

t1   =   2;     //调用ival(2)产生临时对象后

                  //在调用operator   =   函数完成赋值操作

   

test   t2(t1);   //调用test(int   i)对t2进行初始化

return   0;

}

对于test   t2   =   test(1);   语句

  因为是初始化,根据c++语法编译器解析时确定它的确应该

调用复制构造函数test(const   test&   t),所以当,test(const   test&   t)   拷贝构造函数为private时会编译出错,但是,编译器在代码生成时会做一定的优化,不同的编译器这种优化程度也可能不同,如是对于test   t2   =   test(1);初始化过程所需要产生不必要临时对象的过程被优化掉了,而其实际调用过程同test   t1   =   1;初始化的调用是一样的,看以下的反汇编代码:

test   t1   =   test(1);    

            //可以看出这一段代码和下一段test   t3   =   1;的反汇编代码一样

            //而其调用过程也是一样的

0041182E     push                 1        

00411830     lea                   ecx,[t1]  

00411833     call                 test::test   (4111C7h)   //调用test(int   i)

test   t3   =   1;

00411838     push                 1        

0041183A     lea                   ecx,[t3]  

0041183D     call                 test::test   (4111C7h)   //调用test(int   i)

t1   =   2;     //调用ival(2)产生临时对象后

00411842     push                 2        

00411844     lea                   ecx,[ebp-0ECh]  

0041184A     call                 test::test   (4111C7h)   //调用test(int   i)

0041184F     lea                   eax,[ebp-0ECh]  

00411855     push                 eax    

00411856     lea                   ecx,[t1]  

00411859     call                 test::operator=   (4111EAh)     //在调用operator   =   函数完成赋值操作

   

test   t2(t1);   //调用test(int   i)对t2进行初始化

0041185E     lea                   eax,[t1]  

00411861     push                 eax    

00411862     lea                   ecx,[t2]  

00411865     call                 test::test   (41125Dh)     //调用test(const   test&   t)

A f=22; 

按常理,先生成一个临时对象,用22初始化,既相当于A f=A(22); 
然后调用拷贝构造函数,将f初始化 

但是由于编译器的优化,如果一个临时变量直接赋值给一个刚生成的还未初始化的变量时(这里是f),会直接把临时量构造在未初始化的对象所在内存中,所以只调用了依次构造函数而已 

这样省掉一块内存空间,而且少调用了一次构造函


原创粉丝点击