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 的说法:
- 直接使用一个对象构造一个对象,如 T t2 = t; 或是 T t2(t);
- 将一个对象以值(不是引用)的形式递给一个函数,比如 test(t);
- 将函数中的一个对象的值(不是引用)传递出函数外,比如 fun();
- 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)。
-
#include <iostream>
-
using namespace std ;
-
-
class Test {
-
public :
-
Test ( ) { value = 0 ; cout << "default constructor." << "\n" ; }
-
Test (Test & t ) { value = 42 ; cout << "non-const copy constructor." << "\n" ; } // can modify t.value !
-
Test ( const Test & t ) { cout << "const copy constructor." << "\n" ; }
-
Test (Test && t ) : value (t. value ) { cout << "move constructor." << "\n" ; }
-
-
~Test ( ) { cout << "destructor." << "\n" ; }
-
-
int getValue ( ) { return value ; } ;
-
private :
-
int value ;
-
} ;
-
-
Test fun (Test tmp )
-
{
-
Test a ;
-
return a ;
-
}
-
-
-
int main ( )
-
{
-
Test a ;
-
cout << "a.value:" << a. getValue ( ) << "\n" ;
-
-
Test b = fun (a ) ;
-
cout << "b.value:" << b. getValue ( ) << "\n" ;
-
-
Test c = a ;
-
cout << "c.value:" << c. getValue ( ) << "\n" ;
-
-
return 0 ;
-
-
}
类中变量的初始化顺序
c++ 中类变量的初始化顺序,大体如下:
- 基类的静态成员初始化;
- 派生类的静态成员初始化;
- 基类的对象成员初始化;
- 基类的构造函数;
- 派生类的对象成员初始化;
- 派生类的构造函数;
貌似 c++11 允许类中变量直接初始化,跟 Java 的使用方法类似,me 么试过。其次,类中对象成员的初始化顺序跟构造函数中初始化列表的顺序无关,只跟声明时候的顺序有关。
测试程序:
-
#include <iostream>
-
using namespace std ;
-
-
class Inner {
-
public :
-
Inner ( int i = 0 ) : in (i ) { cout << "Inner.constructor." << "\n" ; }
-
Inner (Inner & inner ) : in (inner. in ) { cout << "Inner.copy consturctor" << "\n" ; } ;
-
int getValue ( ) { return in ; }
-
-
private :
-
int in ;
-
} ;
-
-
class Inner2 {
-
public :
-
Inner2 ( double i = 0.0 ) : in2 (i ) { cout << "Inner2.constructor." << "\n" ; }
-
Inner2 (Inner2 & inner2 ) : in2 (inner2. in2 ) { cout << "Inner2.copy consturctor" << "\n" ; } ;
-
int getValue ( ) { return in2 ; }
-
private :
-
double in2 ;
-
} ;
-
-
class Base {
-
public :
-
Base ( int v = 0 ) : value (v ) { value = 1 ; cout << "Base.constructor." << "\n" ; }
-
Base (Base & b ) : value (b. value ) { cout << "Base.copy consturctor" << "\n" ; }
-
int getValue ( ) { return value ; }
-
protected :
-
int value ;
-
} ;
-
-
class Derive : public Base {
-
public :
-
// 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";};
-
Derive (string s = "hello", int base = 0, int i = 0, double i2 = 0 ) { cout << "Derive.consturctor" << "\n" ; } ;
-
Derive (Derive & d ) : derive (d. derive ) { cout << "Derive.copy consturctor" << "\n" ; }
-
void printValue ( ) { cout << "derive.base.value == " << value << ", derive.derive == " << derive << ", derive.in == " << in. getValue ( ) << ", derive.in2 == " <<in2. getValue ( ) << "\n" ; }
-
-
private :
-
string derive ;
-
Inner in ;
-
Inner2 in2 ;
-
} ;
-
-
int main ( )
-
{
-
Derive d ( "world", 10, 20, 30.0 ) ;
-
d. printValue ( ) ;
-
// Derive d2 = d;
-
cout << endl ;
-
}