拷贝构造函数和赋值表达式的区别

来源:互联网 发布:好看的电影推荐 知乎 编辑:程序博客网 时间:2024/06/05 07:39

拷贝构造函数和赋值表达式的区别

一、引入

    拷贝构造函数,是用新创建的对象来初始化某内存区域,对于一个已经被初始化的对象来进行operator=操作。

class   A;   

A a;

A b=a;   //拷贝构造函数调用

A b(a);   //拷贝构造函数调用

A a;

A b;

=a;   //赋值运算符调用

拷贝构造函数首先是一个构造函数,它调用的时候产生一个对象,是通过参数传进来的那个对象来初始化,产生的对象。

operator=();是把一个对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检查一下两个对象是不是同一个对象,如果是的话就不做任何操作。

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指向堆中分配的一段内存空间。

二、比较

1、拷贝构造函数

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

{

CExample theObjone;

theObjone.Init40);

//现在需要另一个对象,需要将他初始化称对象一的状态

CExample theObjtwo=theObjone;

...

}

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

其完成方式是内存拷贝,复制所有成员的值。

完成后,theObjtwo.pBuffer==theObjone.pBuffer

即它们将指向同样的地方,指针虽然复制了,但所指向的空间并没有复制,而是由两个对象共用了。(这也是深拷贝和浅拷贝的问题)这样不符合要求,对象之间不独立了,并为空间的删除带来隐患。所以需要采用必要的手段来避免此类情况。

回顾具体过程:首先建立对象theObjtwo,并调用其构造函数,然后成员被拷贝。可以在构造函数中添加操作来解决指针成员的问题。

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

拷贝构造函数的格式为:构造函数名(对象的引用)

提供了拷贝构造函数后的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传给构造函数,以用来作复制。

原则上,应该为所有包含动态分配成员的类都提供拷贝构造函数。

2)拷贝构造函数的另一种调用。

当对象直接作为参数传给函数时,函数将建立对象的临时拷贝,这个拷贝过程也将调同拷贝构造函数。例如

BOOL testfunc(CExample obj);

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

BOOL testfunc(CExample obj)

{

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

}

3)还有一种情况,也是与临时对象有关的

当函数中的局部对象被返回给函数调者时,也将建立此局部对象的一个临时拷贝,拷贝构造函数也将被调用

CTest func()

{

CTest theTest;

return theTest

}

2、赋值符的重载

下面的代码与上例相似

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

{

CExample theObjone;

theObjone.Init(40);

CExample theObjthree;

theObjthree.Init(60);

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

theObjthree=theObjone;

return 0;

}

也用到了"="号,但与上面例子并不同。上面例子中,"="在对象声明语句中,表示初始化。更多时候,这种初始化也可用括号表示。

例如 CExample theObjone(theObjtwo);

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

"="的缺省操作只是将成员变量的值相应复制。旧的值被自然丢弃。

由于对象内包含指针,将造成不良后果:指针的值被丢弃了,但指针指向的内容并未释放。指针的值被复制了,但指针所指内容并未复制。

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

类定义变为:

class CExample

{

...

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

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

...

};

//赋值操作符重载

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

}

3、拷贝构造函数使用赋值运算符重载的代码

CExample::CExample(const CExample& RightSides)

{

pBuffer=NULL;

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

}

4用一个已存在的对象去构造一个不存在的对象(构造之前不存在),就是拷贝构造用一个已存在的对象去覆盖另一个已存在的对象,就是赋值运算

In C++, objects can be copied by assignment or by initialisation. Copying 

by initialisation corresponds to creating an object and initialising its value 

through the copy constructor. The copy constructor has its first argument as a 

reference, or const reference to the object's class type. It can have more 

arguments, if default values are provided. Copying by assignment applies to 

an existing object and is performed through the assignment operator (=). The 

copy constructor implements this for identical type objects:

class MyObject {

public:

MyObject();      // Default constructor

MyObject(MyObject const & a);  // Copy constructor

MyObject & operator = (MyObject const & a) // Assignment operator

}

The copy constructors play an important role, as they are called when 

class objects are passed by value, returned by value, or thrown as an 

exception.

// A function declaration with an argument of type MyObject,

// passed by value, and returning a MyObject

MyObject f(MyObject x) 

{

MyObject r;

...

return(r);  // Copy constructor is called here

}

// Calling the function :

MyObject a;

f(a);        // Copy constructor called for a

It should be noted that the C++ syntax is ambiguous for the assignment 

operator. MyObject x; x=y; and MyObject x=y; have different meaning.

MyObject a;        // default constructor call

MyObject b(a);     // copy constructor call

MyObject bb = a;   // identical to bb(a) : copy constructor call

MyObject c;        // default constructor call

c = a;    // assignment operator call

    As a general rule, objects which implements reference sharing on their

 data members have a copy constructor which shares the data, while the 

assignment operator copies or duplicate the data. 

    如果不主动编写拷贝构造函数和赋值函数,编译器将以位拷贝的方式自动生成缺省的函数。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。类String拷贝构造函数与普通构造函数的区别是:在函数入口处无需与NULL 进行比较,这是因为引用不可能是NULL,而指针可以为NULL

    String的赋值函数分四步实现:

1)第一步,检查自赋值。

注意不要将检查自赋值的if 语句

if(this==&other)错写成为if(*this==other),因为我们比较的是地址,即两个对象是不是相同的。

2)第二步,用delete 释放原有的内存资源。如果现在不释放,以后就没机会了,将造成内存泄露。

3)第三步,分配新的内存资源,并复制字符串。注意函数strlen返回的是有效字符串长度,不包含结束符‘/0’。函数strcpy则连‘/0’一起复制。

4)第四步,返回本对象的引用,目的是为了实现象a=b=c这样的链式表达。注意不要将return *this错写成 return this,因为我们返回的是对象

    当进行一个类的实例初始化的时候,也就是构造的时候,调用的是构造函数(如是用其他实例来初始化,则调用拷贝构造函数),非初始化的时候对这个实例进行赋值调用的是赋值运算符。

#include "iostream"

using namespace std;

class String

{

public:

String(){}

String(const char *str = NULL);     // 普通构造函数

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

~String(void);         // 析构函数

String& operator=(const String &other); //赋值函数

private:

char   *m_String;    //私有成员,保存字符串

};

String::~String(void)   

{

cout<<"Destructing"<<endl;

delete[] m_String;     

}

String::String(const char *str)     

{

cout<<"Construcing"<<endl;

if(str==NULL)        

{

m_String = new char[1];   

*m_String = '/0';    

}     

else

{

int length=strlen(str); 

m_String=new char[length+1];       

strcpy(m_String, str);      

}//else

} 

String::String(const String &other) 

{ 

cout<<"Constructing Copy"<<endl;

int length=strlen(other.m_String); 

m_String = new char[length+1];        

strcpy(m_String, other.m_String);        

}

String& String::operator=(const String &other)  

{ 

cout<<"Operate = Function"<<endl;

//检查自赋值 

if(this==&other) //比较的是指针

return *this;

//释放原有的内存资源         

delete[] m_String;

//分配新的内存资源,并复制内容

int length=strlen(other.m_String); 

m_String=new char[length+1];        

strcpy(m_String, other.m_String);

//返回本对象的引用 

return *this;

} 

int main()

{

String a("Hello");

String b("Hi");

String c(a);

c=b;

}

原创粉丝点击