中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++淺拷貝與深拷貝及引用計數分析

發布時間:2020-10-03 01:32:34 來源:腳本之家 閱讀:119 作者:aidear_evo 欄目:編程語言

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;
};

類A用到了引用計數,構造和拷貝構造函數都初始化為1,attach()函數為引用加1,detach()函數為引用減1,當引用計數值為0時delete對象。類B中的成員變量有個指針指向A,拷貝構造函數中調用了attach(),析構函數中調用了detach(),這樣也是一種保護,不會有內存泄露,也不會有double free,log如下。

int main()
{
  B e;
  B f(e);
  return 0;
}
A::A
B::B
B::B copy-constructor
A::attach
B::~B
A::detach 2
B::~B
A::detach 1
A::~A

從log中可以看出,指針成員變量的引用計數為2,這是正確的,最后正確delete,沒有問題。

在類中只要有指針成員變量,就要注意以上問題,另外,operator=這個賦值操作符也要在適當的時候進行重載。有時候,如果想規避以上問題,可以聲明拷貝構造函數和operator=操作符為private而不去實現它們。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

博乐市| 崇文区| 云南省| 莱阳市| 紫金县| 富平县| 红原县| 湘乡市| 肥西县| 麻阳| 巫山县| 渑池县| 彰武县| 化德县| 比如县| 九江市| 华宁县| 泰来县| 渑池县| 宣汉县| 体育| 巴东县| 宾川县| 静海县| 海城市| 焦作市| 郧西县| 自治县| 台南市| 麦盖提县| 咸阳市| 揭西县| 河源市| 苏州市| 大同县| 塔城市| 祥云县| 新密市| 千阳县| 讷河市| 兴仁县|