• c++ 深度拷贝和浅拷贝


    一、简介

    由于20年转行之后一直用的是halcon  和c# ,C++就此搁浅,最近开始搞pcl慢慢的又重拾起来,对于深拷贝和浅拷贝我知道是什么原因造成的,也知道如何解决,但是突然被人问得时候又有点蒙蔽,因此做一个简单的总结。

    先看浅拷贝和深拷贝的区别:

    浅拷贝:只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,进行简单的赋值拷贝运算,地址是一样的

    深拷贝:new 出来的,在堆区申请了内存空间,

     二、简单剖析

    先举一个例子:作为一个足球迷,真心希望梅西能2022 拿下世界杯,对我们这些没有天赋的普通人来说,天赋比努力要重要100000倍(当你真正的努力过你就知道天赋有多么重要)。

    1. class WorldCupChampion
    2. {
    3. public:
    4. string country;
    5. int* years;
    6. WorldCupChampion();
    7. WorldCupChampion(std::string country, int years);
    8. ~WorldCupChampion();
    9. };
    10. WorldCupChampion::WorldCupChampion()
    11. {
    12. cout << "默认构造" << endl;
    13. }
    14. WorldCupChampion::WorldCupChampion(string country, int years)
    15. {
    16. this->country = country;
    17. this->years = new int(years);
    18. cout << "调用有参构造 " << *(this->years) << " " << this->country << endl;
    19. }
    20. WorldCupChampion::~WorldCupChampion()
    21. {
    22. cout << "调用析构函数 " << endl;
    23. if (years != NULL) // 由于year是new 在堆区的所以我们要在程序结束的时候将其delete掉
    24. {
    25. delete years;
    26. years = NULL;
    27. }
    28. }

    然后我们在复制一个放到b 里面

    1. void test()
    2. {
    3. WorldCupChampion a("阿根廷", 2022);
    4. cout << *(a.years) << "世界杯冠军是 : " << a.country << endl;
    5. WorldCupChampion b(a);
    6. cout << *(b.years) << "世界杯冠军是 : " << b.country << endl;
    7. }

    此时我们进行编译:ok 

    运行:

     我们采用断掉调试,毕竟一切皆地址:

     分析一下:我们在 创建一个对象a,然后赋值b ,将a 的所有的东西都复制给b ,但是,包括new 出来的东西,从上面的测试可以看出,我们现在的拷贝years 的地址是一样的,也就是说a 和b 的指针都指向了同一块内存空间。

     那么当程序退出的时候我们要析构掉,当a 析构的时候已经将2022 这块内存空间已经析构了,那么当b 再次析构的时候 已经没有这块

    内存了,导致程序报错。

    浅拷贝结论:

    浅拷贝带来的问题得本质是析构函数多长释放堆空间

    三、深拷贝

    为了解决上面的问题:我们自定义了一个拷贝---深拷贝

    模式如下:

    WorldCupChampion(const  WorldCupChampion& a);

    我们需要在这个构造当中重复new 出一片空间来存储year 

    1. WorldCupChampion::WorldCupChampion(string country, int years)
    2. {
    3. this->country = country;
    4. this->years = new int(years);
    5. cout << "调用有参构造 " << *(this->years) << " " << this->country << endl;
    6. }

    下面我们先看结果:

     

    OK,那我们再次使用断点来看看这次他两的地址是不是一样的:

     完整代码:

    1. #if 1
    2. class WorldCupChampion
    3. {
    4. public:
    5. string country;
    6. int* years;
    7. WorldCupChampion();
    8. WorldCupChampion(std::string country, int years);
    9. WorldCupChampion(const WorldCupChampion& a);
    10. ~WorldCupChampion();
    11. };
    12. WorldCupChampion::WorldCupChampion()
    13. {
    14. cout << "默认构造" << endl;
    15. }
    16. WorldCupChampion::WorldCupChampion(const WorldCupChampion& a)
    17. {
    18. cout << "深度拷贝" << endl;
    19. this->country = a.country;
    20. this->years = new int(*a.years);
    21. }
    22. WorldCupChampion::WorldCupChampion(string country, int years)
    23. {
    24. this->country = country;
    25. this->years = new int(years);
    26. cout << "调用有参构造 " << *(this->years) << " " << this->country << endl;
    27. }
    28. WorldCupChampion::~WorldCupChampion()
    29. {
    30. cout << "调用析构函数 " << endl;
    31. if (years != NULL) // 由于year是new 在堆区的所以我们要在程序结束的时候将其delete掉
    32. {
    33. delete years;
    34. years = NULL;
    35. }
    36. }
    37. void test()
    38. {
    39. WorldCupChampion a("阿根廷", 2022);
    40. cout << *(a.years) << "世界杯冠军是 : " << a.country << endl;
    41. WorldCupChampion b(a);
    42. cout << *(b.years) << "世界杯冠军是 : " << b.country << endl;
    43. }
    44. int main()
    45. {
    46. test();
    47. return 0;
    48. }
    49. #endif

    结论: 

    使用才会精进,一旦不使用,最简单的问题也会被遗忘 

  • 相关阅读:
    Java基础---File类、文件过滤器、字节流
    【JavaScript复习十】数组入门知识
    AWS API gateway api CORS错误处理方法
    使用Redis实现文章阅读量、收藏、点赞数量记录功能
    使用 vllm 本地部署 Llama3-8b-Instruct
    学校介绍静态HTML网页设计作品 DIV布局学校官网模板代码 DW大学网站制作成品下载 HTML5期末大作业
    Linux:安装IDEA开发工具
    新书推荐——华为·无线局域网应用技术(微课版丨第2版)
    HTML & CSS入门:从基础到实践
    VS编程技巧——写好枚举后自动补全switch
  • 原文地址:https://blog.csdn.net/weixin_39354845/article/details/128009878