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

来源:互联网 发布:淘宝修身包臀连衣裙 编辑:程序博客网 时间:2024/06/05 23:47

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 0