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

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

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

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

      淺拷貝和深拷貝
      函數

        在某些情況下,類內成員變量須要動態開闢堆內存,若是實行位拷貝,也就是把對象裏的值徹底複製給另外一個對象,如A=B。這時,若是B中有一個成員變量指針已經申請了內存,那A中的那個成員變量也指向同一塊內存。這就出現了問題:當B把內存釋放了(如:析構),這時A內的指針就是野指針了,出現運行錯誤。spa

        深拷貝和淺拷貝能夠簡單理解爲:若是一個類擁有資源,當這個類的對象發生複製過程的時候,資源從新分配,這個過程就是深拷貝,反之,沒有從新分配資源,就是淺拷貝。下面舉個深拷貝的例子。


      指針

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

      1.淺拷貝:code

         淺拷貝就好比像引用類型對象

         淺拷貝是指源對象與拷貝對象共用一份實體,僅僅是引用的變量不一樣(名稱不一樣)。對其中任何一個對象的改動都會影響另一個對象。舉個例子,一我的一開始叫張三,後來更名叫李四了,但是仍是同一我的,無論是張三缺胳膊少腿仍是李四缺胳膊少腿,都是這我的倒黴。blog

      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()地址不是一回事。

相關文章
相關標籤/搜索