zoukankan      html  css  js  c++  java
  • C++ | 深拷贝与浅拷贝的认识和区别

    1、构造函数

    在C++面向对象程序设计中,通过构造函数对对象进程初始化,它可以为对象在计算机内存中开辟内存空间,也可以为对象的数据成员提供初始值。构造函数时一个与类同名,没有返回值的特殊成员函数,每当创建一个对象时(包括使用new动态创建对象),编译系统就会自动调用构造函数。构造函数像类以外的一般函数和类成员函数一样,可以重载和带缺省参数,构造函数的重载为对象的生成提供了各种灵活的手段。

     构造函数分为缺省构造函数(默认构造函数)和用户自定义构造函数。当程序员没有定义构造函数时,系统就会提供一个无参的缺省构造函数,如果用户自定义了一个构造函数,编译器提供的缺省构造函数就自动消失了。


     

    2、拷贝构造函数(复制构造函数)

    拷贝构造函数的功能是一个已有的对象来初始化一个被创建的同类的对象,是一种特殊的构造函数,具有一般构造函数的所有特性,其形参是本类对象的引用。用户可以根据自己实际问题的需要定义特定的拷贝构造函数,以实现同类对象之间数据成员的传递。如果用户没有声明类的拷贝构造函数,系统就会自动生成一个缺省拷贝构造函数,这个缺省拷贝构造函数的功能是把初始的每个数据成员的值都复制到新建立的对象中。拷贝构造函数的声明如下:

            类名(类型 & 对象名);

    class people

    {

    public:

            void dipalay()

            {

            }

    private:

            int a;

            char*name;

    };

    调用拷贝构造函数有以下三种情况:

    (1)用类的一个对象去初始化另一个对象时。

    (2)对象作为函数参数传递时,调用拷贝构造函数

    (3)如果函数的返回值是类的对象,函数调用返回时,调用拷贝构造函数。


     

    3、浅拷贝和深拷贝

    拷贝就是复制,创建副本。假设有对象A,A有属性t1,t2。那么,我通过拷贝A,得到B,B应该也有属性t1,t2,且A、B两个对象的每个属性,都应该是相同的。

    对于基本类型的属性t1,拷贝是没有疑义的。简单将值复制一份,就达到了拷贝的效果。而对于引用类型的属性t2来说,拷贝就有了两层含义。

    第一层是,我只是将t2引用的地址复制一份给B的t2,确实达到了属性相同的效果,可以理解为实现了拷贝,但是事实上,两个对象中的属性t2对应的是同一个对象。在B对象上对t2所指向的对象进行操作,就会影响到A对象中的t2的值。

    第二层是,我将A的t2所指向的对象,假设为o1,完整复制一份,假设为o2,将新的o2的地址给B的t2。也达到了复制的效果,且对B的t2所指向的o2进行操作,不会影响到A的t2所指向的o1。

    拷贝的两层含义,对应了浅拷贝和深拷贝的概念,做了第一层,就是浅拷贝,做到第二层,就是深拷贝。

    基于以上内容,很容易可以想到,浅拷贝比深拷贝要更快,但是,从拷贝的意义上来看,浅拷贝相较于深拷贝,要欠缺一点。


     

    总结一下:

    浅拷贝:位拷贝,拷贝构造函数,赋值重载

    多个对象共用同一块资源,同一块资源释放多次,崩溃或者内存泄漏

    深拷贝:每个对象共同拥有自己的资源,必须显式提供拷贝构造函数和赋值运算符。

    简而言之:深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。


     

    实例如下:

    #include <iostream>using namespace std;

    class CopyDemo{public:  CopyDemo(int pa,char *cstr)  //构造函数,两个参数  {    this->a = pa;    this->str = new char[1024]; //指针数组,动态的用new在堆上分配存储空间    strcpy(this->str,cstr);    //拷贝过来  }

    //没写,C++会自动帮忙写一个复制构造函数,浅拷贝只复制指针,如下注释部分  //CopyDemo(CopyDemo& obj)    //{  //  this->a = obj.a;  //  this->str = obj.str; //这里是浅复制会出问题,要深复制  //}

      CopyDemo(CopyDemo& obj)  //一般数据成员有指针要自己写复制构造函数,如下  {    this->a = obj.a;    // this->str = obj.str; //这里是浅复制会出问题,要深复制    this->str = new char[1024];//应该这样写    if(str != 0)        strcpy(this->str,obj.str); //如果成功,把内容复制过来  }

      ~CopyDemo()  //析构函数  {    delete str;  }

    public:    int a;  //定义一个整型的数据成员    char *str; //字符串指针};

    int main(){  CopyDemo A(100,"hello!!!");

      CopyDemo B = A;  //复制构造函数,把A的10和hello!!!复制给B  cout <<"A:"<< A.a << "," <<A.str << endl;  //输出A:100,hello!!!  cout <<"B:"<< B.a << "," <<B.str << endl;  //输出B:100,hello!!!

      //修改后,发现A,B都被改变,原因就是浅复制,A,B指针指向同一地方,修改后都改变  B.a = 80;  B.str[0] = 'k';

      cout <<"A:"<< A.a << "," <<A.str << endl;  //输出A:100,kello!!!  cout <<"B:"<< B.a << "," <<B.str << endl;  //输出B:80,kello!!!

      return 0;}

    根据上面实例可以看到,浅复制仅复制对象本身(其中包括是指针的成员),这样不同被复制对象的成员中的对应非空指针会指向同一对象,被成员指针引用的对象成为共享的,无法直接通过指针成员安全地删除(因为若直接删除,另外对象中的指针就会无效,形成所谓的野指针,而访问无效指针是危险的;除非这些指针有引用计数或者其它手段确保被指对象的所有权);

     而深复制在浅复制的基础上,连同指针指向的对象也一起复制,代价比较高,但是相对容易管理。

    小结:

    深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。

    假设B复制了A,修改A的时候,看B是否发生变化:

    如果B跟着也变了,说明是浅拷贝,拿人手短!(修改堆内存中的同一个值)

    如果B没有改变,说明是深拷贝,自食其力!(修改堆内存中的不同的值)

    浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,

    深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,

    使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误。


     

    如果你想更好的提升你的编程能力,学好C语言C++编程!弯道超车,快人一步!

    C语言C++学习企鹅圈子】,分享(源码、项目实战视频、项目笔记,基础入门教程)

    欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!

    编程学习书籍:


     

    编程学习视频:


     

    原文链接:https://mp.weixin.qq.com/s/emgZO229CTrtiJidAojpHA

  • 相关阅读:
    CSS hack
    百度前端技术学院-task1.8源代码
    不用bootstrap,只用CSS创建网格布局
    【转】CSS3的calc()使用——精缩版
    百度前端技术学院task16源代码
    【转】正则表达式 匹配中文,英文字母和数字及_的写法!同时控制长度
    【转】潜说js对象和数组
    百度前端技术学院-task1.3源代码
    百度前端技术学院task15源代码
    百度前端技术学院task14源代码
  • 原文地址:https://www.cnblogs.com/mu-ge/p/14289887.html
Copyright © 2011-2022 走看看