一、 什么是浅复制
我想用一种形象的说法来说明这个问题。这里我的定义是不够严谨的,只是适合于C++的初学者。
首先明确在C++中复制这个动作在内存中做了些什么?它先得到一个内存区域,然后再把要复制的东西“填进”这个区域。而所谓的“浅复制”并不是这样进行复制的,它仅仅是让一个指针指向要复制的区域。
二、 一个关于浅复制的简单例子
这里定义了Inner和Outter两个类。
class Inner
{
public:
Inner();
~Inner();
public:
int min1;
};
class Outter
{
public:
Outter(int m_1);//让pInner->min1等于min1
~Outter();
public:
Inner* pInner;
};
Outter::~Outter()
{
cout<<\"called the Destructors of Outter.\"<<endl;
delete pInner;
}
其它方法的实现很简单,这里就不写出来了。代码贴在最后面。下面有个问题,如果在一个函数(如main)中我们写了这几行代码,有会什么结果?
void print(Outter mOutter);
int main()
{
Outter mOut1(20);
print(mOut1);
cout<<mOut1.pInner->min1<<endl;
return 0;
}
print函数并不在那两个类中,它的实现如下:
void print(Outter mOutter)
{
cout<<\"called print().\"<<endl;
cout<<mOutter.pInner->min1<<endl;
}
结果是,print函数结果正常,也就是20。但是,在main打印出的原来那个对象(mOut1)的min1值却变了,成了一个很小的负数,程序也出现异常了。如果去掉print(mOut1)这一行,结果也是20。那么问题就一定是出在print函数中了。可是我们用什么方法传递参数呢?我们是按“值传递”啊!按“理”说,出了这个函数后mOut1.pInner->min1的数据应该不会改变才对,可是事实它就是变了。
“值传递”在这里会为目标对象生成源对象的一个副本,更直接地说就是在内存会分配一个空间,将mOut1的数据复制进来。我们没定义复制构造函数,这时C++编绎器会为我们自动生成一个。但是很不幸,复制的工作没有做“深”,只是表面在让mOutter.pInner = mOut1.pInner,而不是让mOutter.pInner指向一个新开的区域,然后再往里面填相关的数据。所以当mOutter出作用域时,它会自动调用Outter的析构函数。由于我们在析构函数里用了delete pInner(想想为什么要用这个),它当然把所指的内存区域释放掉。所以,在print后我们用cout<<mOut1.pInner->min1<<endl一定得不到正确的结果,而这句之后,main函数退出过程也要调用Outter的析构函数,delete空区域当然不行了,所以就出现异常。 [Page]
三、 问题的解决
前面我们分析了问题的根源,现在想想应该怎么解决这个问题?这个问题的解决比查出它的原因容易得多。我们只需把没有做“深”的事情做“深”就可以了。既然C++编程器的复制构造函数“不好用”,我们就自己写一个吧。代码如下:
Outter::Outter(const Outter& mOut)
{
cout<<\"called the Copy Constructors of Outter.\"<<endl;
pInner = new Inner();//重新分配一个区域,把工作做“深”
pInner->min1 = mOut.pInner->min1;
}
加上const是保证mOut的数据在这个函数中不会被修改。
下面把代码贴上,在VC++2005中能正常运行,其它编绎器我没试过。
#include <iostream>
using namespace std;
class Inner
{
public:
Inner();
~Inner();
public:
int min1;
};
Inner::Inner()
{
cout<<"called the Constructors of Inner."<<endl;
}
Inner::~Inner()
{
cout<<"called the Destructors of Inner."<<endl;
}
class Outter
{
public:
Outter(int m_1);
Outter(const Outter& mOut);
~Outter();
public:
Inner* pInner;
};
Outter::Outter(int m_1)
{
cout<<"called the Constructors of Outter."<<endl;
pInner = new Inner;
pInner->min1 = m_1;
}
Outter::Outter(const Outter& mOut)
{
cout<<"called the Copy Constructors of Outter."<<endl;
pInner = new Inner();
pInner->min1 = mOut.pInner->min1;
}
Outter::~Outter()
{
cout<<"called the Destructors of Outter."<<endl;
delete pInner;
}
void print(Outter mOutter)
{
cout<<"called print()."<<endl;
cout<<mOutter.pInner->min1<<endl;
}
int main()
{
Outter mOut1(20);
print(mOut1);
cout<<mOut1.pInner->min1<<endl;
return 0;
}