浅拷贝:又称值拷贝,将源对象的值拷贝到目标对象中去,本质上来说源对象和目标对象共用一份实体,只是所引用的变量名不同,地址其实还是相同的。举个简单的例子,你的小名叫西西,大名叫冬冬,当别人叫你西西或者冬冬的时候你都会答应,这两个名字虽然不相同,但是都指的是你。

  假设有一个String类,String s1;String s2(s1);在进行拷贝构造的时候将对象s1里的值全部拷贝到对象s2里。

  我们现在来简单的实现一下这个类

 1 #include <iostream>
 2 #include<cstring>
 3
 4 using namespace std;
 5
 6 class STRING
 7 {
 8 public:
 9     STRING( const char* s = "" ) :_str( new char[strlen(s)+1] )
10
11     {
12         strcpy_s( _str, strlen(s)+1, s );
13     }
14     STRING( const STRING& s )
15     {
16         _str = s._str;
17     }
18     STRING& operator=(const STRING& s)
19     {
20         if (this != &s)
21         {
22             this->_str = s._str;
23         }
24         return *this;
25     }
26
27     ~STRING()
28     {
29         cout << "~STRING" << endl;
30         if (_str)
31         {
32             delete[] _str;
33             _str = NULL;
34         }
35     }
36
37     void show()
38     {
39         cout << _str << endl;
40     }
41 private:
42     char* _str;
43 };
44
45 int main()
46 {
47     STRING s1("hello linux");
48     STRING s2(s1);
49     s2.show();
50
51     return 0;
52 }

其实这个程序是存在问题的,什么问题呢?我们想一下,创建s2的时候程序必然会去调用拷贝构造函数,这时候拷贝构造仅仅只是完成了值拷贝,导致两个指针指向了同一块内存区域。随着程序的运行结束,又去调用析构函数,先是s2去调用析构函数,释放了它指向的内存区域,接着s1又去调用析构函数,这时候析构函数企图释放一块已经被释放的内存区域,程序将会崩溃。s1和s2的关系就是这样的:

进行调试程序发现s1和s2确实指向了同一块区域:

所以程序会崩溃是应该的,那么这个问题应该怎么去解决呢?这就引出了深拷贝。

深拷贝,拷贝的时候先开辟出和源对象大小一样的空间,然后将源对象里的内容拷贝到目标对象中去,这样两个指针就指向了不同的内存位置。并且里面的内容是一样的,这样不但达到了我们想要的目的,还不会出现问题,两个指针先后去调用析构函数,分别释放自己所指向的位置。即为每次增加一个指针,便申请一块新的内存,并让这个指针指向新的内存,深拷贝情况下,不会出现重复释放同一块内存的错误。

深拷贝实际上是这样的:

深拷贝的拷贝构造函数和赋值运算符的重载传统实现:

 1 STRING( const STRING& s )
 2     {
 3         //_str = s._str;
 4         _str = new char[strlen(s._str) + 1];
 5         strcpy_s( _str, strlen(s._str) + 1, s._str );
 6     }
 7     STRING& operator=(const STRING& s)
 8     {
 9         if (this != &s)
10         {
11             //this->_str = s._str;
12             delete[] _str;
13             this->_str = new char[strlen(s._str) + 1];
14             strcpy_s(this->_str, strlen(s._str) + 1, s._str);
15         }
16         return *this;
17     }

这里的拷贝构造函数我们很容易理解,先开辟出和源对象一样大的内存区域,然后将需要拷贝的数据复制到目标拷贝对象,

那么这里的赋值运算符的重载是怎么样做的呢?

这种方法解决了我们的指针悬挂问题,通过不断的开空间让不同的指针指向不同的内存,以防止同一块内存被释放两次的问题,还有一种深拷贝的现代写法:

 1 STRING( const STRING& s ):_str(NULL)
 2     {
 3         STRING tmp(s._str);// 调用了构造函数,完成了空间的开辟以及值的拷贝
 4         swap(this->_str, tmp._str); //交换tmp和目标拷贝对象所指向的内容
 5     }
 6
 7     STRING& operator=(const STRING& s)
 8     {
 9         if ( this != &s )//不让自己给自己赋值
10         {
11             STRING tmp(s._str);//调用构造函数完成空间的开辟以及赋值工作
12             swap(this->_str, tmp._str);//交换tmp和目标拷贝对象所指向的内容
13         }
14         return *this;
15     }

先来分析一下拷贝构造是怎么实现的:

拷贝构造调用完成之后,会接着去调用析构函数来销毁局部对象tmp,按照这种思路,不难可以想到s2的值一定和拷贝构造里的tmp的值一样,指向同一块内存区域,通过调试可以看出来:

在拷贝构造函数里的tmp:

调用完拷贝构造后的s2:(此时tmp被析构)

可以看到s2的地址值和拷贝构造里的tmp的地址值是一样

关于赋值运算符的重载还可以这样来写:

STRING& operator=(STRING s)
{
  swap(_str, s._str);
  return *this;
}

 1 #include <iostream>
 2 #include<cstring>
 3
 4 using namespace std;
 5
 6 class STRING
 7 {
 8 public:
 9     STRING( const char* s = "" ) :_str( new char[strlen(s)+1] )
10
11     {
12         strcpy_s( _str, strlen(s)+1, s );
13     }
14     //STRING( const STRING& s )
15     //{
16     //    //_str = s._str; //浅拷贝的写法
17     //    cout << "拷贝构造函数" << endl;
18     //    _str = new char[strlen(s._str) + 1];
19     //    strcpy_s( _str, strlen(s._str) + 1, s._str );
20     //}
21     //STRING& operator=(const STRING& s)
22     //{
23     //    cout << "运算符重载" << endl;
24     //    if (this != &s)
25     //    {
26     //        //this->_str = s._str; //浅拷贝的写法
27     //        delete[] _str;
28     //        this->_str = new char[strlen(s._str) + 1];
29     //        strcpy_s(this->_str, strlen(s._str) + 1, s._str);
30     //    }
31     //    return *this;
32     //}
33
34     STRING( const STRING& s ):_str(NULL)
35     {
36         STRING tmp(s._str);// 调用了构造函数,完成了空间的开辟以及值的拷贝
37         swap(this->_str, tmp._str); //交换tmp和目标拷贝对象所指向的内容
38     }
39
40     STRING& operator=(const STRING& s)
41     {
42         if ( this != &s )//不让自己给自己赋值
43         {
44             STRING tmp(s._str);//调用构造函数完成空间的开辟以及赋值工作
45             swap(this->_str, tmp._str);//交换tmp和目标拷贝对象所指向的内容
46         }
47         return *this;
48     }
49
50     ~STRING()
51     {
52         cout << "~STRING" << endl;
53         if (_str)
54         {
55             delete[] _str;
56             _str = NULL;
57         }
58     }
59
60     void show()
61     {
62         cout << _str << endl;
63     }
64 private:
65     char* _str;
66 };
67
68 int main()
69 {
70     //STRING s1("hello linux");
71     //STRING s2(s1);
72     //STRING s2 = s1;
73     //s2.show();
74     const char* str = "hello linux!";
75     STRING  s1(str);
76     STRING s2;
77     s2 = s1;
78     s1.show();
79     s2.show();
80
81     return 0;
82 }

参考与

浅析C++中的深浅拷贝 - qq_39344902的博客 - CSDN博客
https://blog.csdn.net/qq_39344902/article/details/79798297

转载于:https://www.cnblogs.com/cxq0017/p/10617313.html

C++ 中的深拷贝与浅拷贝相关推荐

  1. python怎么避免浅拷贝_详谈Python中的深拷贝和浅拷贝

    在平时工作中,经常涉及到数据的传递,在数据传递使用过程中,可能会发生数据被修改的问题.为了防止数据被修改,就需要在传递一个副本,即使副本被修改,也不会影响原数据的使用.为了生成这个副本,就产生了拷贝. ...

  2. python中的深拷贝与浅拷贝

    浅拷贝的时候,修改原来的对象,深拷贝的对象不会发生改变. 对象的赋值 对象的赋值实际上是对象之间的引用:当创建一个对象,然后将这个对象赋值给另外一个变量的时候,python并没有拷贝这个对象,而只是拷 ...

  3. C语言中的深拷贝和浅拷贝

    http://www.cnblogs.com/zhanggaofeng/p/5421804.html C语言中的深拷贝和浅拷贝 //C语言中的深拷贝和浅拷贝 #define _CRT_SECURE_N ...

  4. python中关于深拷贝和浅拷贝的详解

    python中关于深拷贝和浅拷贝的详解 概述 在python的语法中,有两种变量的拷贝方式 一种是深拷贝,一种是浅拷贝 我们先说深拷贝 语法 这里需要通过导入系统的copy模块中的deepcopy才可 ...

  5. vb.net中递归退到最外层_面试题被问到再也不慌,深究JavaScript中的深拷贝与浅拷贝...

    " 点个关注,养成习惯,带你python爬虫的过程中学习前端 " JavaScript中的深拷贝和浅拷贝是前端面试中频繁被问到的一道题, 于是我也自己去查阅了一些资料, 然后动手敲 ...

  6. js中的深拷贝和浅拷贝区别

    js中的深拷贝和浅拷贝与值传递和引用传递有着些许的关联,都是根据堆栈中数据的储存来传递数据. 下面主要说一下我对深拷贝和浅拷贝的理解: 简单举个例子来说明:当我们声明一个a变量并且赋值,并且让b等于a ...

  7. 详谈Javascript中的深拷贝和浅拷贝

    数据复制是我们编程中经常会使用到的技术,对于普通数值数据来说,复制很简单,但是对于复杂类型比如对象的复制,就会有很多需要考虑的东西,比如我们经常说到的深拷贝和浅拷贝. 浅拷贝 复制的对象和原始对象属性 ...

  8. Javascript中的深拷贝和浅拷贝

    文章目录 JavaScript中的变量类型 深拷贝和浅拷贝的理解 深拷贝和浅拷贝的实现方式 为什么需要深拷贝和浅拷贝 JavaScript中的变量类型 (1).基本类型 JavaScript中的基本类 ...

  9. 低门槛彻底理解JavaScript中的深拷贝和浅拷贝

    在说深拷贝与浅拷贝前,我们先看两个简单的案例: //案例1 var num1 = 1, num2 = num1; console.log(num1) //1 console.log(num2) //1 ...

  10. python中的深拷贝_Python中的深拷贝和浅拷贝

    前言:在认识深浅拷贝的时候,先了解python中的可变类型与不可变类型. 以及 python中的传参到底是传递值还是传递引用(内存地址) python中的可变数据类型主要有 :(列表,字典) 指的是在 ...

最新文章

  1. [云炬python3玩转机器学习笔记] 3-4创建Numpy数组和矩阵
  2. 用姓名字段统计人数_2019年度全国各地姓名报告分析汇总(全国、深圳、佛山、杭州)...
  3. 【Python】random库的使用
  4. IBatis.Net学习笔记七--日志处理
  5. 不更新安装包,实现文件更新 奕婷特许授权发布
  6. You have to specify ‘-keep‘ options for the shrinking step
  7. php 模拟微信登录,实例详解PHP实现微信模拟登陆并给用户发送消息
  8. 《人工智能及其应用》重点回顾
  9. 大数据杀熟已被明令禁止!
  10. 符号-$,美元符号$在不同语言代码中的解释与应用
  11. Vue实现 侧边固定定位图标 滑动隐藏
  12. 如何让CDC类USB设备批量接收64字节以上数据
  13. Word文件的只读模式没有密码怎么退出?
  14. Iptables—包过滤(网络层)防火墙
  15. 老司机手把手教php,老司机手把手教你玩驱魔!纯小白无脑驱魔攻略
  16. 变电站气象站(电力微气象监测站)是什么?
  17. 计算机视觉--实时车速检测
  18. (一)DragonBone制作运动的小汽车
  19. 中国首个开源基金会“开放原子开源基金会”亮相
  20. 电商企业如何选择ERP管理软件

热门文章

  1. 游戏筑基开发之结构体(数组、指针)、枚举、共用体、typdef(C语言)
  2. [Luogu1216][USACO1.5]数字三角形 Number Triangles
  3. 文本分类——NaiveBayes
  4. 大数据奏鸣曲,听出了什么?
  5. C语言入门经典(第5版)
  6. 系统登录界面(收集)
  7. GNU make manual 翻译(三)
  8. 第十二课:实验二 循环链表实验
  9. linux主从库配置文件,Linux系统中MongoDB安装及主从配置linux操作系统 -电脑资料
  10. static_cast,reinterpret_cast,const_cast,dynamic_cast: