虽然对象复制看上去很简单,然而如果你没有对其正确理解,可能会出现一些严重问题。默认情况下,复制对象会导致相应的所有成员的复制。如果你只有实例成员,这看上去是相当不错的。但是如果你的类中含有指向在堆中分配的对象时,情况会怎样呢?考虑下面的代码片断:#include <stdio.h>
#include <string.h>
class person
{
private:
char* _name;
public:
person()
{
_name = new char[256];
}
void setname(const char* name)
{
if(strlen(name) + 1 < 256)
strcpy(_name,name);
}
void printname()
{
printf("%s\n",_name);
}
};
int main()
{
// 创建对象的第一个实例并赋于名字为john
person p1;
p1.setname("john");
p1.printname();
//通过复制p1引用的对象创建另一个对象
person p2(p1);
p2.setname("alice");
p2.printname();
//现在再输出p1的名字
p1.printname();
scanf("q");
return 0;
}
这里的类person有一个指向在堆上分配的字符数组的指针。当构造person对象时,它创建该字符数组并把它的位置存放到变量_name中。
但是当你创建person 对象 p2 时,p2的成员用p1的成员初始化。因而,p1的 _name与p2的 _name指向相同的堆对象。如在上例中看到的,调用p2.setname将改变由这两个类共享的值。所以,当第二次调用p1.printname,打印结果是"alice"。
所以,这不是我们复制对象所期望的结果,而且还会导致堆崩溃的问题。请再考虑某个函数删除了该数组而p1又要调用该函数的情况?下面,当p2调用printname时,它将尽量存取实际上不是在堆上的对象。这种情况下产生的结果往往是难以预料的。
c++允许我们通过定义拷贝构造函数来克服这类问题。在我们每次通过复制另一个对象来初始化一个对象时,拷贝构造函数都被执行。你可以在拷贝构造函数中覆盖掉缺省的成员函数的复制行为。
所以,我们的类person应该修改如下:class person
{
private:
char* _name;
public:
person()
{
_name = new char[256];
}
// 这是拷贝构造函数。在此我们初始化一个新的数组,为person的实例所用
person(person&)
{
_name = new char[256];
}
void setname(const char* name)
{
if(strlen(name) + 1 < 256)
strcpy(_name,name);
}
void printname()
{
printf("%s\n",_name);
}
};
这里类person中的拷贝构造函数保证了它初始化一个新的数组,为在复制时产生的每一个对象实例所用。这就避免了前面我们提到的问题。
希望上面所述能够帮助读者理解拷贝构造函数及其使用场所。
Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器