c++构造函数以及类中变量初始化顺序

c++构造函数以及类中变量初始化顺序

构造函数

c++ 的类提供了一种抽象机制,使用起来要比 c 方便很多。为了安全等,c++ 中的每一个对象都要经过构造,跑出作用域之外的对象(非 free-store 上的)都要析构,不管是使用自定义的构造/析构函数也好,还是使用默认的。为了对象的正确拷贝构造或是赋值,me 们要自己写拷贝构造函数/拷贝赋值,为了所谓的效率,还得自己写移动构造函数/移动赋值。默认生成的若干个函数有:

Test();    // 默认构造函数,可以不带参数的构造函数,T t;
Test(Test& t);    // 拷贝构造函数,T t2 = t;
Test(Test&& t);    // 移动构造函数,T t3 = fun(); fun() 返回一个 T 的临时对象
Test& operate=(Test& t);  // 拷贝赋值,t3 = t2;
Test& operate=(Test&& t);    // 移动赋值,t2 = fun();
~Test();    // 析构函数

默认构造函数比较简单,两个移动 (move) 函数是 c++11 中加入的,其实也可以先不关心,拷贝赋值也先不关心,就说拷贝构造函数,神马时候会调用的问题,根据 wiki 的说法:

  1. 直接使用一个对象构造一个对象,如 T t2 = t; 或是 T t2(t);
  2. 将一个对象以值(不是引用)的形式递给一个函数,比如 test(t);
  3. 将函数中的一个对象的值(不是引用)传递出函数外,比如 fun();
  4. throw 一个对象,catch 一个对象的值(不是引用);

基本规则是上面说的,但是却不是总是那样,据说 c++标准允许编译器优化拷贝,实际上 gcc 在函数返回值是否拷贝的问题上很多时候跟 vc++ 就不一致。有一项技术叫 RVO (return value optimization ) —— 返回值优化,貌似就是说的那个。me 的实际运行结果是:gcc 在函数返回一个对象的值的时候可能不调用拷贝构造函数/移动构造函数,而 vc++ 就要循规蹈矩得多一些,虽然说是数据多拷了几次。真的是 maybe,即使是 vc++ 在函数中直接 return T(); 和 Test t; return t; 结果都可能不一样,gcc 中的结果可能更离奇一点。

很多时候真不用关心这一点细节问题,拷贝也不会出什么问题。不过拷贝构造 maybe 有“副作用”,比如不是等值拷贝,而是放大一倍拷贝,这个时候,如果拷贝构造函数调用的次数都不一样,程序结果可能就不一样,实际上是,结果的确就不一样!所以,拷贝还是尽可能遵循原有的“语义”,等值拷贝,否则都是合法的 c++程序,在 vc 和 gcc 中结果不一样让人有些惊讶!

现在说,移动构造的问题,可以先看下右值引用。有了移动构造的话,有时候就影响拷贝构造,比如 T t=fun(); 以前肯定会说是调用“拷贝构造函数”,现在就不一定了,可能就是“移动构造函数”了,也就是直接将返回来的临时对象,作为新构造的对象使用;而按其那面 gcc 的逻辑,可能这里神马“构造函数”都不调用,因为就没有构造!如果说对于 A a = testA(); 会调用“移动构造”,而 B b = testB(); 却不调用“移动构造”,这也令 me 很震惊,都是返回一个临时对象,都有移动构造函数,为嘛有时候就调用,有时候就不调用?O__O"…竟然跟里面是神马变量有关?!!!可以修改下面的程序,自己看运行结果:(移动构造是 c++11 新加的,gcc 编译的话,需要加上 -std=c++11,or =std=c++0x)。

  1. #include <iostream>
  2. using  namespace std ;
  3.  
  4. class Test {
  5. public :
  6.     Test ( ) { value  =  0 ;   cout  <<  "default constructor."  <<  "\n" ;  }
  7.     Test (Test & t ) {  value  =  42 ;  cout  <<  "non-const copy constructor."  <<  "\n" ;  }     // can modify t.value !
  8.     Test ( const Test & t ) {     cout  <<  "const copy constructor."  <<  "\n" ;  }
  9.     Test (Test && t ) : value (t. value ) {  cout  <<  "move constructor."  <<  "\n" ;  }
  10.  
  11.     ~Test ( ) {    cout  <<  "destructor."  <<  "\n" ;  }
  12.  
  13.      int getValue ( ) {  return value ;  } ;
  14. private :
  15.      int value ;
  16. } ;
  17.  
  18. Test fun (Test tmp )
  19. {
  20.     Test a ;
  21.      return a ;
  22. }
  23.  
  24.  
  25. int main ( )
  26. {
  27.     Test a ;
  28.      cout  <<  "a.value:"  << a. getValue ( )  <<  "\n" ;
  29.  
  30.     Test b  = fun (a ) ;
  31.      cout  <<  "b.value:"  << b. getValue ( )  <<  "\n" ;
  32.  
  33.     Test c  = a ;
  34.      cout  <<  "c.value:"  << c. getValue ( )  <<  "\n" ;
  35.  
  36.      return  0 ;
  37.  
  38. }

类中变量的初始化顺序

c++ 中类变量的初始化顺序,大体如下:

  1. 基类的静态成员初始化;
  2. 派生类的静态成员初始化;
  3. 基类的对象成员初始化;
  4. 基类的构造函数;
  5. 派生类的对象成员初始化;
  6. 派生类的构造函数;

貌似 c++11 允许类中变量直接初始化,跟 Java 的使用方法类似,me 么试过。其次,类中对象成员的初始化顺序跟构造函数中初始化列表的顺序无关,只跟声明时候的顺序有关。

测试程序:

  1. #include <iostream>
  2. using  namespace std ;
  3.  
  4. class Inner {
  5. public :
  6.     Inner ( int i = 0 ) : in (i )  {  cout  <<  "Inner.constructor."  <<  "\n" ; }
  7.     Inner (Inner & inner ) : in (inner. in )  {  cout  <<  "Inner.copy consturctor"  <<  "\n" ; } ;
  8.      int getValue ( ) {  return in ;  }
  9.  
  10. private :
  11.      int in ;
  12. } ;
  13.  
  14. class Inner2 {
  15. public :
  16.     Inner2 ( double i = 0.0 ) : in2 (i )  {  cout  <<  "Inner2.constructor."  <<  "\n" ; }
  17.     Inner2 (Inner2 & inner2 ) : in2 (inner2. in2 )  {  cout  <<  "Inner2.copy consturctor"  <<  "\n" ; } ;
  18.      int getValue ( ) {  return in2 ;  }
  19. private :
  20.      double in2 ;
  21. } ;
  22.  
  23. class Base {
  24. public :
  25.     Base ( int v = 0 ) : value (v )  { value  =  1 ;  cout  <<  "Base.constructor."  <<  "\n" ; }
  26.     Base (Base & b ) : value (b. value )  {  cout  <<  "Base.copy consturctor"  <<  "\n" ; }
  27.      int getValue ( ) {  return value ;  }
  28. protected :
  29.      int value ;
  30. } ;
  31.  
  32. class Derive :  public Base {
  33. public :
  34.      // Derive(string s = "hello", int base=0, int i=0, double i2=0): derive(s), in2(i2), in(i) { cout << "derive.derive == " << derive << ", Derive.consturctor" << "\n";};
  35.      Derive (string s  =  "hello"int base = 0int i = 0double i2 = 0 ) {  cout  <<  "Derive.consturctor"  <<  "\n" ; } ;
  36.     Derive (Derive & d ) : derive (d. derive )  {  cout  <<  "Derive.copy consturctor"  <<  "\n" ; }
  37.      void printValue ( ) {   cout  <<  "derive.base.value == "  << value  <<  ", derive.derive == "  << derive  <<  ", derive.in == "  << in. getValue ( )  <<  ", derive.in2 == "  <<in2. getValue ( )  <<  "\n" ;  }
  38.  
  39. private :
  40.     string derive ;
  41.     Inner in ;
  42.     Inner2 in2 ;
  43. } ;
  44.  
  45. int main ( )
  46. {
  47.     Derive d ( "world"102030.0 ) ;
  48.     d. printValue ( ) ;
  49.      // Derive d2 = d;
  50.      cout  << endl ;
  51. }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值