一.为什么会有拷贝构造函数
#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
}
结语:
在以后出现了继承和组合时,涉及到类的层次结构,对象的按值传递是一个很麻烦的事情。拷贝构造函数的重要性将会在那里得到体现。就会有深复制和浅复制两种方式来实现拷贝构造函数。