拷贝构造函数

来源:互联网 发布:b站视频mac 编辑:程序博客网 时间:2024/06/05 09:47

复制初始化

以下讨论中将用到的例子:

class CExample

{

public:

CExample(){pBuffer=NULL; nSize=0;}

~CExample(){delete []pBuffer;}

void Init(int n){ pBuffer=new char[n]; nSize=n;}

private:

char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源

int nSize;

};

这个类的主要特点是包含指向其他资源的指针pBuffer指向堆中动态分配的一段内存空间。

int main(int argc, char* argv[])

{

CExample theObjone;

theObjone.Init(40);

//现在需要另一个对象,并将它初始化为theObjone

CExample theObjtwo=theObjone;

...

}

语句"CExample theObjtwo=theObjone;"theObjone初始化theObjtwo

回顾一下此语句的具体过程:首先建立对象theObjtwo,并调用其构造函数,然后成员被复制初始化。

其完成方式是内存拷贝,复制所有成员的值。完成后,theObjtwo.pBuffer==theObjone.pBuffer

即它们将指向同样的地方,指针虽然复制了,但所指向的空间并没有复制,而是由两个对象共用了。这样不符合要求,对象之间不独立了,并为空间的删除带来隐患。所以需要采用必要的手段来避免此类情况:可以在构造函数中添加操作来解决指针成员的这种问题。

所以C++语法中除了提供缺省形式的构造函数外,还规范了另一种特殊的构造函数:拷贝构造函数,一种特殊的构造函数重载。上面的语句中,如果类中定义了拷贝构造函数,在对象复制初始化时,调用的将是拷贝构造函数,而不是缺省构造函数。在拷贝构造函数中,可以根据传入的变量,复制指针所指向的资源。

拷贝构造函数的格式为:类名(const类名& 对象名);//拷贝构造函数的原型,参数是常量对象的引用。由于拷贝构造函数的目的是成员复制,不应修改原对象,所以建议使用const关键字

提供了拷贝构造函数后的CExample类定义为:

class CExample

{

public:

CExample(){pBuffer=NULL; nSize=0;}

~CExample(){delete []pBuffer;}

CExample(const CExample&); //拷贝构造函数

void Init(int n){ pBuffer=new char[n]; nSize=n;}

private:

char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源

int nSize;

};

CExample::CExample(const CExample& RightSides) //拷贝构造函数的定义

{

nSize=RightSides.nSize; //复制常规成员

pBuffer=new char[nSize]; //复制指针指向的内容

memcpy(pBuffer,RightSides.pBuffer,nSize*sizeof(char));

}

这样,定义新对象,并用已有对象初始化新对象时,CExample(const CExample& RightSides)将被调用,而已有对象用别名RightSides传给构造函数,以用来作复制。

对象按值传递

下面介绍拷贝构造函数的另一种调用:当对象直接作为参数传给函数时,函数将建立对象的临时拷贝,这个拷贝过程也将调用拷贝构造函数。例如:

BOOL testfunc(CExample obj);

testfunc(theObjone); //对象直接作为参数。

BOOL testfunc(CExample obj)

{

//针对obj的操作实际上是针对复制后的临时拷贝进行的

}

还有一种情况,也是与临时对象有关:当函数中的局部对象作为返回值被返回给函数调者时,也将建立此局部对象的一个临时拷贝,拷贝构造函数也将被调用。

CTest func()

{

CTest theTest;

return theTest;

}

总结:当某对象是按值传递时(无论是作为函数参数,还是作为函数返回值),编译器都会先建立一个此对象的临时拷贝,而在建立该临时拷贝时就会调用类的拷贝构造函数。

赋值重载

重载的必要性

下面的代码与上例相似

int main(int argc, char* argv[])

{

CExample theObjone;

theObjone.Init(40);

CExample theObjthree;

theObjthree.init(60);

//现在需要一个对象赋值操作,被赋值对象的原内容被清除,并用右边对象的内容填充。

theObjthree=theObjone;

return 0;

}

这里也用到了"="号,但与"复制初始化"中的例子并不同。"复制初始化"的例子中,"="在对象声明语句中,表示初始化。更多时候,这种初始化也可用圆括号表示。例如:CExample theObjthree(theObjone);

而本例子中,"="表示赋值操作。将对象theObjone的内容复制到对象theObjthree,这其中涉及到对象theObjthree原有内容的丢弃,新内容的复制。

"="的缺省操作只是将成员变量的值相应复制。由于对象内包含指针,将造成不良后果:指针的值被丢弃了,但指针指向的内容并未释放。指针的值被复制了,但指针所指内容并未被复制。

因此,包含动态分配成员的类除提供拷贝构造函数外,还应该考虑重载"="赋值操作符号

重载的示例

类定义变为:

class CExample

{

public:

CExample(){pBuffer=NULL; nSize=0;}

~CExample(){delete pBuffer;}

CExample(const CExample&); //拷贝构造函数

CExample& operator = (const CExample&); //赋值符重载

void Init(int n){ pBuffer=new char[n]; nSize=n;}

private:

char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源

int nSize;

};

//赋值操作符重载

CExample& CExample::operator = (const CExample& RightSides)

{

if (this == &RightSides) // 如果自己给自己赋值则直接返回

{return *this;}

nSize=RightSides.nSize;//复制常规成员

char *temp=new char[nSize]; //复制指针指向的内容

memcpy(temp,RightSides.pBuffer,nSize*sizeof(char));

delete []pBuffer;//删除原指针指向内容(将删除操作放在后面,避免X=X特殊情况下,内容的丢失)

pBuffer=temp;//建立新指向

return *this;

}

重载的注意事宜

拷贝构造函数和赋值函数的功能是相同的,为了不造成重复代码,拷贝构造函数实现如下:

CExample::CExample(const CExample& RightSides)

{

*this=RightSides; //调用重载后的"="

}

格式示例

拷贝构造函数的格式

拷贝构造函数的声明:

class 类名

{

public

类名(形参参数)//构造函数的声明/原型

类名(类名&对象名)//拷贝构造函数的声明/原型

...

};

拷贝构造函数的实现:

类名::类名(类名&对象名)//拷贝构造函数的实现/定义

{函数体}

拷贝构造函数的示例

Class Point

{

Public:

Point(int xx=0,int yy=m)(X=xx;Y=yy;)

Point(Point& p);

Int getX() {return X}

Int getY(){ return Y;}

Private :

Int X,Y;

}

Point::Point(Point& p)

{

X=p.X;

Y=p.Y;

Cout<<"拷贝构造函数调用"<<endl;

}


0 0
原创粉丝点击