一.为什么会有拷贝构造函数
#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 < 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
三、默认拷贝构造函数
在我们没有为类建立拷贝构造函数的时候,编译器会自动为新类创建一个默认拷贝构造函数,就如第一个例程,但它使用的是简单位拷贝。就是将原始对象的内存简单复制一份,放在新对象的地址上。但这种位拷贝在出现了继承和组合时会出现很多问题。所以一般在写的类稍复杂一点时,我们就必须添加拷贝构造函数。
不过有时因为初始化很困难,自己很难写出新的拷贝构造函数,而默认拷贝构造函数也不能达到我们的要求,这时我们可以强制编译器不允许按值传递对象,“声明一个私有拷贝构造函数”,甚至不必去定义它。
{
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
}
结语:
在以后出现了继承和组合时,涉及到类的层次结构,对象的按值传递是一个很麻烦的事情。拷贝构造函数的重要性将会在那里得到体现。就会有深复制和浅复制两种方式来实现拷贝构造函数。