一.为什么会有拷贝构造函数
#include < fstream > #include < string > using namespace std;ofstream out ( " HowMany.out " ); class HowMany{ private : static int objectCount; public : HowMany() { objectCount ++ ; } static void print( const string & msg = "" ) { if (msg.size() != 0 ) out << msg << " : " ; out << " objectCount= " << objectCount << endl; } ~ HowMany() { objectCount -- ; print( " ~Howmany() " ); }}; int HowMany::objectCount = 0 ;HowMany f(HowMany x){ x.print( " x argument inside f() " ); return x;} int main(){ HowMany h; HowMany::print( " after construction of h " ); HowMany h2 = f(h); HowMany::print( " after call to f() " );}这里是上面的代码的输出: after construction of h: objectCount= 1 x argument inside f(): objectCount= 1 ~Howmany(): objectCount= 0 after call to f(): objectCount= 0 ~Howmany(): objectCount= -1 ~Howmany(): objectCount= -2
为什么会出现这种情况呢?一定是什么地方出了问题。很明显是在函数f 里面。 它是按值传递的参数,这样就会得到原来对象的一份拷贝,是临时的对象,出了f 的范围这个拷贝自动调用析构销毁,并且将对象计数objectCount减一。但是在传递参数的时候调用构造函数时应该将对象计数objectCount加一的啊,这里却没有。说明在函数传递参数的时候没用调用构造函数,而是用的另外一种方式传递的参数,就是拷贝构造函数。
二.怎么用拷贝构造函数
当通过按值传递的方式传递一个对象时,就创立了一个新对象。在下面三种情况会用拷贝构造函数来初始化对象。 1. 函数参数按值传递 (和上面那个例程一样) 2. 返回一个对象来初始化另外一个对象 HowMany f(HowMany t); //函数f 返回HowMany对象 HowMany h1; HowMany h2=f(h1); 3. 直接调用拷贝构造函数 HowMany h1; HowMany h2(h1); 拷贝构造函数和普通的构造函数差不多,都是以类的名字为函数名,无返回值,只是参数有些不同。 形式为 X(X&)
#include < fstream > #include < string > using namespace std;ofstream out ( " HowMany2.out " ); class HowMany2{ private : string name; static int objectCount; public : HowMany2( const string & id = "" ):name(id) { ++ objectCount; print( " HowMany2() " ); } void print( const string & msg = "" ) const { if (msg.size() != 0 ) out << msg << endl; out << " " << name << " : " << " objectCount = " << objectCount << endl; } HowMany2( const HowMany2 & h) { name = h.name; name += " copy " ; ++ objectCount; print( " HowMany2(const HowMany2&) " ); } ~ HowMany2() { objectCount -- ; print( " ~Howmany2() " ); }}; int HowMany2::objectCount = 0 ;HowMany2 f(HowMany2 x){ x.print( " x argument inside f() " ); out << " Returning from f() " << endl; return x;} int main(){ HowMany2 h( " h " ); out << " Entering f() " << endl; HowMany2 h2 = f(h); h2.print( " h2 after call to f() " ); out << " Call f(), no return value " << endl; f(h); out << " After call to f() " << endl; HowMany2 h4(h2); h4.print( " h4 initial " );}这里演示了上述的三种需要使用拷贝构造函数的情况,大家可以仔细分析一下输出: HowMany2(const HowMany2&) h copy: objectCount = 2 x argument inside f() h copy: objectCount = 2 Returning from f() HowMany2(const HowMany2&) h copy copy: objectCount = 3 ~Howmany2() h copy: objectCount = 2 h2 after call to f() h copy copy: objectCount = 2 Call f(), no return value HowMany2(const HowMany2&) h copy: objectCount = 3 x argument inside f() h copy: objectCount = 3 Returning from f() HowMany2(const HowMany2&) h copy copy: objectCount = 4 ~Howmany2() h copy copy: objectCount = 3 ~Howmany2() h copy: objectCount = 2 After call to f() HowMany2(const HowMany2&) h copy copy copy: objectCount = 3 h4 initial h copy copy copy: objectCount = 3 ~Howmany2() h copy copy copy: objectCount = 2 ~Howmany2() h copy copy: objectCount = 1 ~Howmany2() h: objectCount = 0
三、默认拷贝构造函数
在我们没有为类建立拷贝构造函数的时候,编译器会自动为新类创建一个默认拷贝构造函数,就如第一个例程,但它使用的是简单位拷贝。就是将原始对象的内存简单复制一份,放在新对象的地址上。但这种位拷贝在出现了继承和组合时会出现很多问题。所以一般在写的类稍复杂一点时,我们就必须添加拷贝构造函数。 不过有时因为初始化很困难,自己很难写出新的拷贝构造函数,而默认拷贝构造函数也不能达到我们的要求,这时我们可以强制编译器不允许按值传递对象,“声明一个私有拷贝构造函数”,甚至不必去定义它。
class NoCC{ private : int i; NoCC( const NoCC & ); // 私有的拷贝构造函数 public : NoCC( int ii = 0 ):i(ii) {}}; void f(NoCC); int main(){ NoCC n; f(n) // error NoCC n2 = n; // error NoCC n3(n); // error }
结语:
在以后出现了继承和组合时,涉及到类的层次结构,对象的按值传递是一个很麻烦的事情。拷贝构造函数的重要性将会在那里得到体现。就会有深复制和浅复制两种方式来实现拷贝构造函数。