C++浅拷贝与深拷贝及引用计数分析分享

—-想了解C++浅拷贝与深拷贝及引用计数分析分享的全部内容且更多的C语言教程关注<计算机技术网(www.ctvol.com)!!>

C++浅拷贝与深拷贝及引用计数分析

在C++开发中,经常遇到的一个问题就是与指针相关的内存管理问题,稍有不慎,就会造成内存泄露、内存破坏等严重的问题。不像Java一样,没有指针这个概念,所以也就不必担心与指针相关的一系列问题,但C++不同,从C语言沿袭下来的指针是其一大特点,我们常常要使用new/delete来动态管理内存,那么问题来了,特别是伴随着C++的继承机制,如野指针、无效指针使用、内存泄露、double free、堆碎片等等,这些问题就像地雷一样,一不小心就会踩那么几颗。

先来谈一下C++类中常见的浅拷贝问题,以及由此引发的double free。什么是浅拷贝?当类中的成员变量包括指针时,而又没有定义自己的拷贝构造函数,那么在拷贝一个对象的情况下,就会调用其默认拷贝构造函数,其实这个函数没做什么事,只是对其成员变量作了个简单的拷贝,也就是所谓的位拷贝,它们指向的还是同一个存储空间,当对象析构时,就会析构多次,也就是double free,下面举例说明。

  class Common  {  public:    Common()    {      std::cout << "Common::Common" << std::endl;    }      Common(const Common &r)    {      std::cout << "Common::Common copy-constructor" << std::endl;    }      ~Common()    {      std::cout << "Common::~Common" << std::endl;    }  };    

类Common是个一般的类,定义了构造、拷贝构造和析构函数,在函数里输出一些log,用以跟踪函数调用情况。

  class BitCopy  {  public:    BitCopy()      : m_p(new Common)    {      std::cout << "BitCopy::BitCopy" << std::endl;    }      ~BitCopy()    {      std::cout << "BitCopy::~BitCopy" << std::endl;      if (m_p) {        delete m_p;        m_p = NULL;      }    }    private:    Common *m_p;  };    

类BitCopy就是一个浅拷贝类,成员变量是我们刚定义的类指针,构造函数实例化成员变量,析构函数delete成员变量,没有定义拷贝构造函数。

  int main()  {    BitCopy a;    BitCopy b(a);    return 0;  }  log如下:  Common::Common  BitCopy::BitCopy  BitCopy::~BitCopy  Common::~Common  BitCopy::~BitCopy  Common::~Common  *** Error in `./a.out': double free or corruption (fasttop): 0x0000000001f4e010 ***  已放弃 (核心已转储)  

从上面的log可以看出,对象a调用了构造函数,对象b调用的是默认拷贝构造函数,最后析构了两次,从而造成double free,核心已转储即core dump。

针对以上问题,该怎么解决呢?有两个办法,一个是深拷贝,一个是引用计数。先来看一下深拷贝,深拷贝要定义自己的拷贝构造函数,在函数中给成员变量重新分配存储空间,也就是所谓的值拷贝,这样它们所指向的就是不同的存储空间,析构时不会有问题,但这种方法只适用于较小的数据结构,如果数据结构过大,多次分配存储空间之后,剩余的存储空间将逐渐减小,

下面看个例子。

  class ValueCopy  {  public:    ValueCopy()      : m_p(new Common)    {      std::cout << "ValueCopy::ValueCopy" << std::endl;    }      ValueCopy(const ValueCopy &r)      : m_p(new Common(*r.m_p))    {      std::cout << "ValueCopy::ValueCopy copy-constructor" << std::endl;    }      ~ValueCopy()    {      std::cout << "ValueCopy::~ValueCopy" << std::endl;      if (m_p) {        delete m_p;        m_p = NULL;      }    }    private:    Common *m_p;  };    

类ValueCopy是个深拷贝类,与上面例子的浅拷贝类不同的是定义了拷贝构造函数,在函数中给成员变量重新分配存储空间,下面是用法及log。

  int main()  {    ValueCopy c;    ValueCopy d(c);    return 0;  }  Common::Common  ValueCopy::ValueCopy  Common::Common copy-constructor  ValueCopy::ValueCopy copy-constructor  ValueCopy::~ValueCopy  Common::~Common  ValueCopy::~ValueCopy  Common::~Common  

从上面的log可以看出,对象c调用了构造函数,对象d调用的是自定义拷贝构造函数,最后析构了两次而没有问题,可见深拷贝的用处所在。

引用计数与深拷贝不同,方法是共享同一块存储空间,这个对大的数据结构比较有利。使用引用计数,需要在类中定义一个成员变量专门用于计数,初始值为1,后面引用了这个对象就加1,对象销毁时引用减1,但并不真正的delete这个对象,只有当这个成员变量的值为0时才进行delete,例子如下。

  class A  {  public:    A()      : m_refCount(1)    {      std::cout << "A::A" << std::endl;    }      A(const A &r)      : m_refCount(1)    {      std::cout << "A::A copy-constructor" << std::endl;    }      ~A()    {      std::cout << "A::~A" << std::endl;    }      void attach()    {      std::cout << "A::attach" << std::endl;      ++m_refCount;    }      void detach()    {      if (m_refCount != 0) {        std::cout << "A::detach " << m_refCount << std::endl;        if (--m_refCount == 0) {          delete this;        }      }    }    private:    int m_refCount;  };    class B  {  public:    B()      : m_pA(new A)    {      std::cout << "B::B" << std::endl;    }      B(const B &r)      : m_pA(r.m_pA)    {      std::cout << "B::B copy-constructor" << std::endl;      m_pA->attach();    }      ~B()    {      std::cout << "B::~B" << std::endl;      m_pA->detach();    }    private:    A* m_pA;  };    

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/488154.html

(0)
上一篇 2020年11月12日
下一篇 2020年11月12日

精彩推荐