构造函数,拷贝构造和赋值运算符‘=‘的区别
2015-05-27 10:40
204 查看
例子
class CExample
{
private:
char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源
int nSize;
public:
CExample() {pBuffer=NULL;nSize=0;}
~CExample() {delete pBuffer;}
void Init(int n) { pBuffer=new char
; nSize=n;}
};
int main( )
{
CExample theObjone; //对象创建,调用构造函数
theObjone.Init(40);
CExample theObjtwo=theObjone; //对象复制(因初始化),调用拷贝构造函数
(有指针成员,使用默认拷贝构造有缺点(仅复制指针),故要用自定义的)
CExample theObjthree;
theObjthree.Init(60);
theObjthree=theObjone; //对象赋值操作(因非初始化),调用赋值运算符
(有指针成员,使用默赋值符“=”有缺点(旧指针被丢弃),故要重载运算符)
}
注意:"="号的两种不同使用,初始化和赋值
第三行也用到了"="号,该"="在对象声明语句中,表示初始化。更多时候,这种初始化也可用括号()表示。而最后一行的"="号是赋值符,使用默认赋值符"=",是把被赋值对象的原内容被清除,并用右边对象的内容填充。
构造函数 主要用来在创建对象时, 即为对象成员变量赋初始值
拷贝构造函数 形式为X(X&),用来完成基于同一类的其他对象的构建及初始化
一个对象需要通过另外一个对象进行初始化,调用拷贝构造函数。
初始化和赋值的不同含义是构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值操作符共同实现的
默认拷贝构造函数(浅拷贝)
CExample theObjtwo=theObjone;"用theObjone初始化theObjtwo。
调用默认拷贝构造含, 其完成方式是内存拷贝(浅拷贝),复制所有成员的值。
完成后,theObjtwo.pBuffer==theObjone.pBuffer。 即它们将指向同样的地方,指针虽然复制了,但所指向的空间并没有复制,而是由两个对象共用了。
这样不符合要求,对象之间不独立了,并为空间的删除带来隐患(产生野指针)
自定义拷贝构造函数(深拷贝)
因此可以在构造函数中添加操作来解决指针成员的问题,即自定义的拷贝构造函数
//自定义拷贝构造函数
CExample::CExample(const CExample& RightSides)
{
nSize=RightSides.nSize; //复制常规成员
pBuffer=new char[nSize]; //复制指针指向的内容
memcpy(pBuffer,RightSides.pBuffer,nSize*sizeof(char));
}
这样,定义新对象,并用已有对象初始化新对象时,CExample(const CExample& RightSides)将被调用,而已有对象用别名RightSides传给构造函数,以用来作复制。
原则上,应该为所有包含动态分配成员的类都提供自定义的拷贝构造函数。
注意:内存拷贝函数void *memcpy(void *dest, constvoid *src, size_t n)
默认赋值操作符"="
//赋值操作符重载
CExample & CExample::operator = (const CExample& RightSides)
{
nSize=RightSides.nSize; //复制常规成员
char *temp=new char[nSize];//复制指针指向的内容(默认=的缺点)
memcpy(temp,RightSides.pBuffer,nSize*sizeof(char));
delete []pBuffer; //删除原指针指向内容(避免内存泄露)
pBuffer=NULL; //同时把原指针置为NULL(避免野指针)
pBuffer=temp; //建立新指向
return *this ;
}
"="的缺省(默认)操作只是将成员变量的值相应复制,旧的值被自然丢弃
最后一行的"="表示赋值操作,将对象theObjone的内容复制到对象theObjthree,这其中涉及到对象theObjthree原有内容的丢弃,新内容的复制。
由于对象内包含指针,将造成不良后果:指针的值被丢弃了,但指针指向的内容并未释放(导致内存泄露)。指针的值被复制了,但指针所指内容并未复制(导致新指针指的内容丢了)
即产生两方面的问题:丢弃旧指针(导致内存泄露),仅复制新指针(导致内容丢了)
因此,包含动态分配成员的类除了提供拷贝构造函数外,还应该考虑重载"="赋值操作符号
重载"="赋值操作符
class CExample
{
private:
char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源
int nSize;
public:
CExample() {pBuffer=NULL;nSize=0;}
~CExample() {delete pBuffer;}
void Init(int n) { pBuffer=new char
; nSize=n;}
};
int main( )
{
CExample theObjone; //对象创建,调用构造函数
theObjone.Init(40);
CExample theObjtwo=theObjone; //对象复制(因初始化),调用拷贝构造函数
(有指针成员,使用默认拷贝构造有缺点(仅复制指针),故要用自定义的)
CExample theObjthree;
theObjthree.Init(60);
theObjthree=theObjone; //对象赋值操作(因非初始化),调用赋值运算符
(有指针成员,使用默赋值符“=”有缺点(旧指针被丢弃),故要重载运算符)
}
注意:"="号的两种不同使用,初始化和赋值
第三行也用到了"="号,该"="在对象声明语句中,表示初始化。更多时候,这种初始化也可用括号()表示。而最后一行的"="号是赋值符,使用默认赋值符"=",是把被赋值对象的原内容被清除,并用右边对象的内容填充。
构造函数 主要用来在创建对象时, 即为对象成员变量赋初始值
拷贝构造函数 形式为X(X&),用来完成基于同一类的其他对象的构建及初始化
一个对象需要通过另外一个对象进行初始化,调用拷贝构造函数。
初始化和赋值的不同含义是构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值操作符共同实现的
默认拷贝构造函数(浅拷贝)
CExample theObjtwo=theObjone;"用theObjone初始化theObjtwo。
调用默认拷贝构造含, 其完成方式是内存拷贝(浅拷贝),复制所有成员的值。
完成后,theObjtwo.pBuffer==theObjone.pBuffer。 即它们将指向同样的地方,指针虽然复制了,但所指向的空间并没有复制,而是由两个对象共用了。
这样不符合要求,对象之间不独立了,并为空间的删除带来隐患(产生野指针)
自定义拷贝构造函数(深拷贝)
因此可以在构造函数中添加操作来解决指针成员的问题,即自定义的拷贝构造函数
//自定义拷贝构造函数
CExample::CExample(const CExample& RightSides)
{
nSize=RightSides.nSize; //复制常规成员
pBuffer=new char[nSize]; //复制指针指向的内容
memcpy(pBuffer,RightSides.pBuffer,nSize*sizeof(char));
}
这样,定义新对象,并用已有对象初始化新对象时,CExample(const CExample& RightSides)将被调用,而已有对象用别名RightSides传给构造函数,以用来作复制。
原则上,应该为所有包含动态分配成员的类都提供自定义的拷贝构造函数。
注意:内存拷贝函数void *memcpy(void *dest, constvoid *src, size_t n)
默认赋值操作符"="
//赋值操作符重载
CExample & CExample::operator = (const CExample& RightSides)
{
nSize=RightSides.nSize; //复制常规成员
char *temp=new char[nSize];//复制指针指向的内容(默认=的缺点)
memcpy(temp,RightSides.pBuffer,nSize*sizeof(char));
delete []pBuffer; //删除原指针指向内容(避免内存泄露)
pBuffer=NULL; //同时把原指针置为NULL(避免野指针)
pBuffer=temp; //建立新指向
return *this ;
}
"="的缺省(默认)操作只是将成员变量的值相应复制,旧的值被自然丢弃
最后一行的"="表示赋值操作,将对象theObjone的内容复制到对象theObjthree,这其中涉及到对象theObjthree原有内容的丢弃,新内容的复制。
由于对象内包含指针,将造成不良后果:指针的值被丢弃了,但指针指向的内容并未释放(导致内存泄露)。指针的值被复制了,但指针所指内容并未复制(导致新指针指的内容丢了)
即产生两方面的问题:丢弃旧指针(导致内存泄露),仅复制新指针(导致内容丢了)
因此,包含动态分配成员的类除了提供拷贝构造函数外,还应该考虑重载"="赋值操作符号
重载"="赋值操作符
相关文章推荐
- C++ 构造函数、析构函数、拷贝构造、赋值运算符
- 字符串类的构造函数,拷贝构造,赋值函数的实现
- C++ 中使用构造函数初始化列表和普通构造函数内部初始化的区别
- Java中普通代码块,构造代码块,构造函数,静态代码块区别
- C++基础-构造函数/析构函数/拷贝构造
- 静态代码块,构造代码块和构造函数的区别
- 继承中构造、析构 与 拷贝构造、赋值中的调用区别
- c++实现日期类(class Date) 构造函数 拷贝构造 操作符重载(输入输出 比较操作 算数运算 自增自减)
- 构造代码块与构造函数区别(2012.2.21)
- C++ Primer笔记9_构造函数_拷贝构造(深拷贝与浅拷贝)
- 面向对象及构造代码块、静态代码块、构造函数的区别
- 号)、sex(性别)、birthday(出生日期)、id(身份证号)等等。其中“出生日期”定义为一个“日期”类内嵌子对象。用成员函数实现对人员信息的录入和显示。要求包括:构造函数和析构函数、拷贝构造函
- 拷贝构造,深度拷贝,关于delete和default相关的操作,explicit,类赋初值,构造函数和析构函数,成员函数和内联函数,关于内存存储,默认参数,静态函数和普通函数,const函数,友元
- 静态代码块、构造代码块与构造函数的区别
- 继承中构造、析构 与 拷贝构造、赋值中的调用区别
- 构造函数,拷贝构造的优化相关问题
- 拷贝构造,构造函数,析构函数的调用顺序
- 构造函数和拷贝构造的N种调⽤情况
- 04 为什么会有构造方法?以及构造函数与set()/get()方法的区别?
- C++ 学习笔记(13)拷贝构造函数、拷贝赋值运算符、移动构造函数、移动赋值运算符、析构函数、右值引用、引用限定符