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

  假设有一个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. Redis入门到精通-Redis高级命令
  2. 2021年,AI有潜力改善农业的十种路径
  3. SQL Server在更改计算机名后的设置
  4. 已知一个掺杂了多个数字字符的中文名拼音,去掉所有数字字符之后,形式为“名”+空格+“姓”;并且名和姓的首字母大写,其他小写,要求输出姓名全拼,且全为小写。(后附详细样例说明)
  5. 【计算机视觉】OpenCV篇(3) - 图像几何变换(仿射变换/透视变换)
  6. TensorFlow实验(2)
  7. 特斯拉将国产Model 3和Model Y后轮驱动版价格上调4752元
  8. 【Python实例第11讲】文本的核外分类
  9. C/C++移位运算符
  10. 开始学习:Ruby On Rails
  11. 最值钱的程序员打法 机器学习从入门到精通50篇+自学python全套教程!
  12. java hsqldb_Java HsqlDB的初步使用和技巧总结
  13. 301跳转有什么用?为什么要做301跳转?
  14. oracle+omf+格式,Oracle OMF管理数据文件
  15. 第三方支付网关的选择
  16. HTTP协议的默认端口是什么?底层原理是什么?
  17. 核心微生物分析_科学网—微生物组核心OTU鉴定usearch otutab_core - 刘永鑫的博文...
  18. python能参加奥赛吗-信息竞赛一定要python吗
  19. js实现移动端电子签名
  20. 【面试题】CSS 中几种最常用的水平垂直居中的方法

热门文章

  1. LVS详解(三)——LVS算法简介
  2. BGP联邦原理及配置实例
  3. CTF中一点进制转换脚本记录
  4. 2019.5.25 Noip模拟测试2 T1题解
  5. Oracle用户密码过期的处理方法
  6. webstorm的下载以及React环境搭建
  7. tortoisegit 还原到某个版本
  8. 简单实用的Android ORM框架TigerDB
  9. 字典树(Trie树)的实现及应用
  10. matlab里面画离散信号怎么画_一、基本离散信号的MATLAB产生和图形实现