C++---淺拷貝、深拷貝、寫時拷貝講解(附代碼)

來源:互聯網
上載者:User

對於普通的類型來說,拷貝沒什麼大不了的。

int a = 0;int b = a;

不會出現任何問題。

而類對象與普通對象不同,類對象內部結構一般較為複雜,存在各種成員變數。

淺拷貝

首先來說說我們常遇到的淺拷貝的情況。

#include <stdio.h> class student{public:    student()      // 建構函式,p指向堆中分配的一空間    {        _name = new char(100);        printf("預設建構函式\n");    }    ~student()     // 解構函式,釋放動態分配的空間    {        if (_name != NULL)        {            delete _name;            _name = NULL;            printf("解構函式\n");        }    }private:    char * _name;     // 一指標成員};int main(){    student a;    student b(a);   // 複製對象    return 0;}

這段代碼乍看之下沒什麼毛病,通過類的預設建構函式將 a 複製給 b ,但是一旦運行就會程式崩潰
經過我的刻苦學習與鑽研,終於發現其中的問題所在。
由於我的類沒有拷貝建構函式,所以student b(a)會調用,編譯器自動產生的一個預設拷貝建構函式,該建構函式完成對象之間的位拷貝。位拷貝又稱淺拷貝
淺拷貝

  • 淺拷貝只是拷貝了指標,並沒有建立新的空間,使得兩個指標指向同一個地址,這樣在對象塊結束,調用函數析構的時,會造成同一份資源析構2次,即delete同一塊記憶體2次,造成程式崩潰。

  • 淺拷貝使得 a 和 b 指向同一塊記憶體,任何一方的變動都會影響到另一方。

  • 由於 a 和 b 指向的是同一塊記憶體空間,當 a 釋放了後,b 指向的記憶體空間不複存在,所以會出現記憶體泄露的情況。

如何避免淺拷貝害人呢?
養成自訂拷貝建構函式的習慣,當顯式定義了拷貝建構函式後,編譯器就會調用拷貝建構函式了,為了不出現程式崩潰,請使用自訂拷貝建構函式,當然我們自己如果把代碼寫成了淺拷貝的形式,那也不是不可能的事。

深拷貝

// 使用自定製拷貝建構函式,完成深拷貝!!!class A{public:    A()      // 建構函式,p指向堆中分配的一空間    {        m_pdata = new char(100);        printf("預設建構函式\n");    }    A(const A& r)    // 拷貝建構函式    {        m_pdata = new char(100);    // 為新對象重新動態分配空間        memcpy(m_pdata, r.m_pdata, strlen(r.m_pdata));        printf("copy建構函式\n");    }    ~A()     // 解構函式,釋放動態分配的空間    {        if (m_pdata != NULL)        {            delete m_pdata;            printf("解構函式\n");        }    }private:    char *m_pdata;     // 一指標成員};int main(){    A a;    A b(a);   // 複製對象    return 0;}

在拷貝建構函式中,為 b 對象 new 了一個新的空間,這樣 a 和 b 指向的是不同的空間,只是內容一致,但是互不影響。
重複的去開闢空間和釋放空間效率是很低的,聰明的地球人決定使用寫時拷貝。

寫時拷貝

寫時拷貝:引入一個計數器,每片不同內容的空間上都再由一個計數器組成,在構造第一個類指向時,計數器初始化為1,之後每次有新的類也指向同一片空間時,計數器加 1 ;在析構時判斷該片空間對應計數器是否為1,為1則執行清理工作,大於1則計數器減 1 。如果有需要進行增刪等操作時,再拷貝空間完成,有利於提高效率。

class String{public:    String(const char* str = "")        :_str(new char[strlen(str) + 1 + 4])//+1表示字串後面要放一個'\0',+4表示多開闢一個空間存放引用計數    {        _str += 4;//_str指向資料存放區        strcpy(_str, str);        _GetCount() = 1;    }    String(const String& s)        :_str(s._str)    {        _GetCount()++;    }    String& operator=(String& s)    {        if (this != &s)        {            if (--_GetCount() == 0)            {                delete[](_str - 4);            }            ++s._GetCount();            _str = s._str;        }        return *this;    }    ~String()    {        if (--_GetCount() == 0)        {            delete[](_str - 4); // 注意:由於計數器存放在了_str首地址-4的地址上,所以在析構時一定要注意全部釋放,避免記憶體流失。        }    }public:    int& _GetCount()    {        return *((int*)_str - 1);    }private:    char* _str;};

相關文章:

C#淺拷貝和深拷貝執行個體解析

Python中的賦值、淺拷貝、深拷貝介紹

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.