博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
类(二)——拷贝控制(浅拷贝,深拷贝,浅赋值,深赋值)
阅读量:5887 次
发布时间:2019-06-19

本文共 3552 字,大约阅读时间需要 11 分钟。

一、拷贝构造函数

浅拷贝:

1、如果类未定义自己的拷贝构造函数,编译器会为它合成一个默认拷贝构造函数(默认合成的是public的)。

拷贝构造函数从来不显式调用,而是由编译器隐式地调用。在以下三种情况:

(1)定义对象

Object a;
Object b(a); // 或写成 Object b = a;

(2)动态创建对象

Object a;
Object* p = new Object(a);

(3)函数的传值调用

void Test(Object obj);

 

2、区分构造与赋值:

构造:
Object a;
Object b = a; // 或写作 Object b(a);
// 此为“构造”,在创建对象的时候给初值,拷贝构造函数被调用

赋值:

Object a(1, 2);
Object b;
b = a; // 此为“赋值”,不会调用拷贝构造函数,调用的是拷贝赋值运算符

 

3、注意:除非深拷贝(即类中含有指针成员时),否则不要人为定义拷贝构造函数,使用编译器合成的默认拷贝构造函数即可。

一旦你决定了要添加拷贝构造函数,请仔细检查:

(1)所有的成员变量,要依次拷贝,所有成员变量,不能遗漏

(2)记得调用父类的拷贝构造函数

 

深拷贝(类中有指针成员):

当类有指针成员的时候,需要人为定义拷贝构造函数,而不能继续使用编译器默认合成的。

因为默认的拷贝构造函数只会单纯的复制指针,而不会把指针指向的对象的值保存到一块新开辟的内存中。

 看如下例子:

1 #include 
2 3 using namespace std; 4 5 class Text 6 { 7 public: 8 Text(string s) 9 {10 size = s.length();11 p = new string(s);12 }13 ~Text()14 {15 delete p; //默认析构函数只会销毁成员指针,不会delete成员指针指向的对象,所以此处要人为定义一个析构函数。16 }17 private:18 int size;19 string* p;20 };21 22 int main()23 {24 Text t1("hello");25 Text t2(t1); //调用拷贝构造函数26 }

此程序运行时崩溃,主要原因是:

// 对象创建

t1 的指针对象p,指向一块内存
t2 拷贝了 t1, 此时 t2.p 和 t1.p 指向了同一块内存

// 对象析构

对象t1析构, t1.p 被delete,释放其指向对象的内存;
对象t2析构,t2.p被delete,但因为 t2.p 和 t1.p 指向了同一块内存,此块内存已经被delete,所以崩溃。(同一块内存被重复delete)

 

解决办法:人为定义自己的拷贝构造函数(深拷贝),程序如下:

1 #include 
2 #include
3 using namespace std; 4 5 class Text 6 { 7 public: 8 Text() = default; //默认构造 9 Text(string s) //构造函数10 {11 size = s.length();12 p = new string(s);13 }14 Text(const Text& t) //需要人为定义自己的深拷贝构造函数15 {16 this -> size = t.size;17 this -> p = new string(*t.p);18 }19 ~Text()20 {21 delete p; //默认析构函数只会销毁成员指针,不会delete成员指针指向的对象,所以此处要人为定义一个析构函数。22 }23 private:24 int size;25 string* p;26 };27 28 int main()29 {30 Text t1("hello");31 Text t2 = t1; //调用拷贝构造函数32 }

 

 

三、拷贝赋值运算符

1与拷贝构造函数一样,如果类未定义自己的拷贝赋值运算符,编译器会为它合成一个默认的拷贝赋值运算符(默认合成的是public的)。  (浅赋值)

2、除非类中含有指针成员时,否则不要人为定义拷贝赋值运算符,使用编译器默认合成的即可。  (深赋值)

 

看如下例子:

1 #include 
2 #include
3 using namespace std; 4 5 class Text 6 { 7 public: 8 Text() = default; //默认构造 9 Text(string s) //构造函数10 {11 size = s.length();12 p = new string(s);13 }14 15 ~Text()16 {17 delete p; //默认析构函数只会销毁成员指针,不会delete成员指针指向的对象,所以此处要人为定义一个析构函数。18 }19 private:20 int size;21 string* p;22 };23 24 int main()25 {26 Text t1("hello");27 Text t2;28 t2 = t1; //调用赋值运算符29 }

运行时会崩溃,原因与深拷贝原因一样,编译器合成的默认赋值运算符只是拷贝了指针,没有新开辟内存,导致同一对象的内存重复被delete。

 

解决方法:深赋值(与深拷贝类似),人为定义一个拷贝赋值运算符

  注意: (1) 要记得释放原左侧对象的内存,之后再把右侧对象赋值给左侧对象。

              (2) 返回值为类类型的引用,即返回本对象

1 #include 
2 #include
3 using namespace std; 4 5 class Text 6 { 7 public: 8 Text() = default; //默认构造 9 Text(string s) //构造函数10 {11 this->size = s.length();12 p = new string(s);13 }14 Text& operator = (const Text& t)15 {16 auto newp = new string(*t.p); //拷贝底层的string17 delete this->p; //释放旧内存18 this->p = newp; //从右侧运算对象拷贝数据到本对象19 this->size = t.size;20 return *this; //返回本对象21 }22 23 ~Text()24 {25 delete p; //默认析构函数只会销毁成员指针,不会delete成员指针指向的对象,所以此处要人为定义一个析构函数。26 }27 private:28 int size;29 string* p;30 };31 32 int main()33 {34 Text t1("hello");35 Text t2;36 t2 = t1; //调用赋值运算符37 }

 

转载于:https://www.cnblogs.com/FengZeng666/p/9340690.html

你可能感兴趣的文章
[转载] Live Writer 配置写 CSDN、BlogBus、cnBlogs、163、sina 博客
查看>>
SQL:连表查询
查看>>
MySQL日期函数、时间函数总结(MySQL 5.X)
查看>>
c语言用尾插法新建链表和输出建好的链表
查看>>
高性能 Oracle JDBC 编程
查看>>
java 中ResultSet可以获取的数据类型及返回值类型列表
查看>>
ubuntu 13 安装SH程序
查看>>
支付宝升级延时到账功能
查看>>
ghost后只剩下一个盘的数据寻回方法
查看>>
输入输出练习
查看>>
Git commit message和工作流规范
查看>>
java面试。答案源于网上
查看>>
yii中取得CActiveDataProvider的分页信息
查看>>
我的大学
查看>>
Google翻译接口收费啦
查看>>
Debian+Apache2服务器
查看>>
MySQL库和表的操作
查看>>
shell编程:编译器、解释器 变量
查看>>
yum仓库一些简单介绍
查看>>
HashMap----工作原理
查看>>