隱式內存共享
Many C++ classes in Qt use implicit data sharing to maximize resource usage and minimize copying. Implicitly shared classes are both safe and efficient when passed as arguments, because only a pointer to the data is passed around, and the data is copied only if and when a function writes to it, i.e., copy-on-write
下面是官方文檔對于隱式轉換說明:
Qt中的許多C++類使用隱式數據共享來最大限度地提高資源使用率并最大限度地減少復制。當作為參數傳遞時,隱式共享類既安全又高效,因為只有指向數據的指針會被傳遞,并且只有當函數向數據寫入時,數據才會被復制,即寫時復制
QT官網:隱式共享
共享類
共享類由指向共享數據塊的指針組成,該數據塊包含引用計數和數據
創建共享對象時,會將引用計數設置為1。每當新對象引用共享數據時,引用計數就會增加,當對象取消引用共享數據后,引用計數會減少。當引用計數為零時,共享數據將被刪除。
qt中所有使用隱式數據共享的類,其賦值運算符**operator=**采用的都是淺拷貝
即只拷貝指針,共享數據塊引用加一,在隱式共享類任何修改其數據的成員函數中,它都會在修改數據之前自動分離出一塊新的內存空間(當共享數據塊中的引用計數為1的時候會直接更改共享數據塊,而不是創建新的內存空間),并對新的內存空間進行修改(除容器迭代器)。
推薦淺拷貝使用
一般情況下我們可以默認使用**opertot=**中的淺拷貝即可,這樣會優化內存空間和拷貝效率,即使我們想改其中的內容也不需要擔心,因為在進行寫入的時候共享類就會在寫入函數中自動進行內存分離,創建新的共享內存塊,修改也只會在新的共享內存塊上修改。
如果真的想顯式進行深拷貝,需要調用共享類的特定函數,例如:QImage的copy()函數
QImage的bits()和constBits()
兩個函數都是返回第一個像素元素地址,bits()返回的是一個變量指針,所以QImage內部當引用計數不為0時會有一次深拷貝,分配一個新的內存空間。constBits()返回的是一個常亮指針,所以QImage內部不會有深拷貝。
Qimage和Qpixmap中的內存共享
QImage和QPixmap類使用隱式數據共享,可以按值傳遞對象,如果沒有修改操作則不會有深拷貝消耗。
1:創建兩個QImage,使用=號賦值,未做修改操作則內存地址相同,引用計數為2
2:QImage作為參數,使用值傳遞,函數內部不對QImage做修改的話,也不會有只拷貝,但共享內存的引用計數會加1。下面使用值拷貝,引用技術變為4,分別為:qimage, qimage1, Test函數內部的行參,局部變量m
3:QPixmap創建時,其內部也有一個共享內存指針,不過使用默認的QPixmap::fromImage()創建時會開辟一個新的空間,可以看到pixmap內存地址和qimage不一樣,且引用計數為1。此時會有一次內存拷貝的消耗
4:當我們從QImage轉換為QPixmap不想要內存拷貝消耗時,例如頻繁的更新圖像顯示,可以添加標志Qt::ImageConversionFlag::NoFormatConversion。下圖可以看到三個變量內存地址相同,引用計數為3。
5:當發生寫入或者修改時,就會出現對共享內存的復制(即深拷貝)。例如下面Qpainter對圖片進行繪制,繪制之前兩個QImage內存相同,繪制開始后則qimage1內存地址發生改變(分配了一個新的空間),qimage引用計數變為1
改變后: