一、简单说明
在C++中,如果我们定义一个空类,编译器会默认为我们自动生成以下四个函数:
1、默认构造函数 2、析构函数 3、拷贝构造函数 4、赋值构造函数。
通常情况下,我们都会显式的定义默认构造函数和析构函数,但是,如果类的成员变量中存在指针变量,一般我们需要显式的定义拷贝构造函数和赋值构造函数,目的是为了避免浅拷贝造成的内存重复释放问题。
一下示例在此类基础上:
class CExample
{
public :
//构造函数
CExample(): pBuffer(NULL), nSize(0)
{
}
CExample(const CExample &); //拷贝构造函数
CExample & operator = (const CExample &); //赋值构造函数
// 析构函数
~CExample()
{
if(pBuffer != NULL)
{
delete pBuffer;
pBuffer = NULL;
}
}
void Init(int n)
{
pBuffer = new char [n];
nSize = n;
}
private :
char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源
int nSize;
};
二、拷贝构造函数
原型:
ClassName(const ClassName &);
示例:
//拷贝构造函数的定义
CExample::CExample(const CExample& other)
{
nSize = other.nSize; //复制常规成员
pBuffer = new char [nSize]; //复制指针指向的内容
memcpy(pBuffer, other.pBuffer, nSize*sizeof (char ));
}
拷贝构造函数被调用的情况:
1、定义新对象,并用已有对象初始化新对象时:即执行语句“CExample B=A; ” 时(定义对象时使用赋值初始化);
2、当对象直接作为参数传给函数时,函数将建立对象的临时拷贝,这个拷贝过程也将调用拷贝构造函数;
例如:
void testfunc(CExample obj)
{
//针对obj的操作实际上是针对复制后的临时拷贝进行的
}
testfunc(theObjone); //对象直接作为参数,拷贝函数将被调用;
3、当函数中的局部对象被返回给函数调者时,也将建立此局部对象的一个临时拷贝,拷贝构造函数也将被调用 。
例如:
CTest func()
{
CTest theTest;
return theTest
}
原型:
ClassName & operator = (const ClassName &);
示例:
//赋值构造函数的定义
CExample & CExample::operator = (const CExample& RightSides)
{
nSize = RightSides.nSize; //复制常规成员
char *temp = new char [nSize]; //复制指针指向的内容
memcpy(temp,RightSides.pBuffer,nSize*sizeof (char ));
delete [] pBuffer; //删除原指针指向内容 (将删除操作放在后面,避免X=X特殊情况下,内容的丢失)
pBuffer = temp; //建立新指向
return *this
}
赋值构造函数被调用的情况:
1、两个已经定义的对象,把一个赋值给另一个,赋值构造函数将被调用
例如:
int main(int argc, char * argv[])
{
CExample A;
A.Init(40);
CExample C;
C.Init(60);
//现在需要一个对象赋值操作,被赋值对象的原内容被清除,并用右边对象的内容填充。
C = A;
return 0;
}
PS: 可以用赋值构造函数来重写拷贝构造函数
//拷贝构造函数调用赋值构造函数
CExample::CExample(const CExample& RightSides)
{
pBuffer = NULL;
*this = RightSides;
}
四、小结
记两个问题:
1、拷贝构造函数与赋值构造函数的区别?
2、拷贝构造函数和赋值构造函数的参数为什么要用引用,而不是指针?