C++中的深拷貝和淺拷貝 QT中的深拷貝,淺拷貝和隱式共享


  • 下面是C++中定義的深,淺拷貝
    • 當用一個已初始化過了的自定義類類型對象去初始化另一個新構造的對象的時候,拷貝構造函數就會被自動調用。也就是說,當類的對象需要拷貝時,拷貝構造函數將會被調用。以下情況都會調用拷貝構造函數:
      (1)一個對象以值傳遞的方式傳入函數體 
      (2)一個對象以值傳遞的方式從函數返回 
      (3)一個對象需要通過另外一個對象進行初始化。

      如果在類中沒有顯式地聲明一個拷貝構造函數,那么,編譯器將會自動生成一個默認的拷貝構造函數,該構造函數完成對象之間的位拷貝。位拷貝又稱淺拷貝,后面將進行說明。

      自定義拷貝構造函數是一種良好的編程風格,它可以阻止編譯器形成默認的拷貝構造函數,提高源碼效率。

      淺拷貝和深拷貝

        在某些狀況下,類內成員變量需要動態開辟堆內存,如果實行位拷貝,也就是把對象里的值完全復制給另一個對象,如A=B。這時,如果B中有一個成員變量指針已經申請了內存,那A中的那個成員變量也指向同一塊內存。這就出現了問題:當B把內存釋放了(如:析構),這時A內的指針就是野指針了,出現運行錯誤。

        深拷貝和淺拷貝可以簡單理解為:如果一個類擁有資源,當這個類的對象發生復制過程的時候,資源重新分配,這個過程就是深拷貝,反之,沒有重新分配資源,就是淺拷貝。下面舉個深拷貝的例子。


    • 總結:有資源的重新分配:深拷貝;無:淺拷貝(直接的調用默認的構造函數實際上也是共享了一款數據的內存,兩個對象都指向這塊數據內存)
  • 下面是QT中的深淺拷貝,我覺得它的東西理解起來比較實用
    •   

      1.淺拷貝:

         淺拷貝就比如像引用類型

         淺拷貝是指源對象與拷貝對象共用一份實體,僅僅是引用的變量不同(名稱不同)。對其中任何一個對象的改動都會影響另外一個對象。舉個例子,一個人一開始叫張三,后來改名叫李四了,可是還是同一個人,不管是張三缺胳膊少腿還是李四缺胳膊少腿,都是這個人倒霉。

      2.深拷貝:

         而深拷貝就比如值類型。改變了數據的內存指向,內存分配發生改變。

      Value(值)對象,如預定義類型Int32,Double,以及結構(struct),枚舉(Enum)等。

      3.隱式共享:

        隱式共享又叫做回寫復制。當兩個對象共享同一份數據時(通過淺拷貝實現數據塊的共享),如果數據不改變,不進行數據的復制。而當某個對象需要改變數據時則執行深拷貝。

    • 實例:
      void MainWindow::on_pushButton_8_clicked()
      {
          QString str1="data";
          qDebug() << " String addr = " << &str1 <<", "<< str1.constData();
          QString str2=str1;  //淺拷貝指向同一個數據塊
          qDebug() << " String addr = " << &str2 <<", "<< str2.constData();
          str2[3]='e';       //一次深拷貝,str2對象指向一個新的、不同於str1所指向的數據結構
          qDebug() << " String addr = " << &str2 <<", "<< str2.constData();
          str2[0]='f';       //不會引起任何形式的拷貝,因為str2指向的數據結構沒有被共享
          qDebug() << " String addr = " << &str2 <<", "<< str2.constData();
          str1=str2;         //str1指向的數據結構將會從內存釋放掉,str1對象指向str2所指向的數據結構
          qDebug() << " String addr = " << &str1 <<", "<< str1.constData();
          qDebug() << " String addr = " << &str2 <<", "<< str2.constData();
      }
      

       

      實測輸出結果如下(括號內是我的分析):

      String addr = 0x28c798 , 0x14316660 (str2的指針地址,指向前面同一個QSharedDataPointer,其實就是data1)
      String addr = 0x28c798 , 0x1433f2a0 (str2的指針地址,指向一個新的QSharedDataPointer,命名為data2)
      String addr = 0x28c798 , 0x1433f2a0 (str2的指針地址,指向data2,但是修改其內容)
      String addr = 0x28c79c , 0x1433f2a0 (str1的指針地址,指向data2,不修改其內容,且放棄data1,使之引用計數為零而被徹底釋放)
      String addr = 0x28c798 , 0x1433f2a0 (str2的指針地址,指向data2,不修改其內容)

      注意,str1的地址和str1.constData()地址不是一回事。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM