隱式共享(copy on write)

隱式共享又稱之為copy on write,意旨當兩個對象共享一份數據時,如果數據不改變,就不進行數據的複製,而當某個對象需要改變數據時,才對資料進行複製。隱式共享可以降低內存和CPU資源的使用效率,像函數的參數或返回值使用值傳遞更有效率。

共享包含了數據本身以及數據的引用計數,當對象創建出來時,引用計數被設置為1,當有新的對象引用到共享數據時,引用計數增加,當有對象不再引用數據時,引用計數減少,當引用計數變為0 時,共享數據被刪除。

在我們操作共享數據時,有深拷貝和淺拷貝兩種拷貝方式,深拷貝會重新複製一個對象;淺拷貝則僅複製指向共享數據塊的指針,因為淺拷貝僅設置一個新的指針,然後將引用計數加1,所以對資源的消耗較少。

  QString str1 = “hello”;  
  QString str2 = str1;    //str2="hello"
  str2[0] = 'a';          //str2="aello" str1="hello"
  str2[0] = 'b';          //str2="bello" str1="hello"
  • QString str2 = str1:在對str2賦值時進行淺拷貝,兩個QString對象都指向同一個數據結構,此結構除了保持字串”hello”外,還保有一個計數器,因為str1和str2皆指向這個數據結構,因此計數器值為2。
  • str2[0] = ‘a’:str2的更改會導致一個深拷貝,使得str2指向一個新的結構,str1和str2指向的結構計數器皆為1。
  • str2[0] = ‘b’:str2的更改不會引起任何的複製,因為str2指向的結構沒有被共享。
  • str1 = str2:str1原本指像的結構計數器變為0,因此指結構會從內存中被釋放掉,str1和str2接指向相同的結構,此結構計數器為2。

Qt許多類別使用了隱式共享技術,且隱式共享是在底層自動完成的,我們不必另外撰寫程式碼,對於QList或者QVector,應該使用at()函數而不是[]操作符進行只讀訪問,原因是Qt容器無法判斷[]操作符是左值還是右值,進而無法隱式數據共享,對於begin(),end()等遍歷器,由於數據可能改變,因此Qt會進行深拷貝,所以當我們容器內物件值不會變動時,盡可能使用const_iterator、constBegin()和constEnd(),這樣會有較佳的效率。