2016-12-05 774 views
0

在控制窗口,我有這個功能我的主類,其中pixmapItem是在類的頭文件中定義的QGraphicsPixmapItem*QImage:如何正確銷燬指向像素數據的指針?

void updateDisplay() { 
    uchar *data = new ...; // array of pixel data 
    ... 
    QImage image = QImage(data, width, height, 
          width, QImage::Format_Indexed8); 

    pixmapItem->setPixmap(QPixmap::fromImage(image)); 
} 

我的問題是:我怎麼能滅data時不再需要它? 「不再需要」意味着上面的函數或我的類中的另一個函數將像素映射設置爲另一個圖像。

我已經看到有QImage的清理功能,可以幫助,但文檔內容不是關於如何使用它們,如何傳遞參數,如圖像的數據指針刪除真正明確。

+0

注意,通常情況下,如果可能的話,這是一個好主意,讓'QImage'本身分配爲像素內存 - 這樣你就可以確保它的大小和對齊正確,並且它由QImage實例擁有/自動銷燬;在構建QImage之後,可以使用「bits」方法獲得指向像素數據的指針。 –

回答

3

QImage被銷燬時,您可以刪除數據;如上面的@JeremyFriesner所示,這可以通過在內部範圍內定義QImage實例來完成,並且可以在其外部分配/解除分配數據。

但是,爲什麼還要打擾所有這些工作?QImage構造函數可以容納複雜的用例,其中已經有來自其他來源的像素數據,或者您需要保持它的「奇怪」生命。

您的情況反而更簡單,像素數據的生命週期與QImage完全一樣,所以最好讓它自己爲像素分配內存;這樣你就可以確定它的大小和排列正確,並且它由QImage實例擁有/自動銷燬;在構建QImage對象後,可以使用bits方法獲得指向像素數據的指針,並根據需要執行任何操作。

這樣一來,你的代碼會簡直成了:

void updateDisplay() { 
    QImage image = QImage(width, height, 
          width, QImage::Format_Indexed8); 
    uchar *data = image.bits(); 
    ... 

    pixmapItem->setPixmap(QPixmap::fromImage(image)); 
} 

更簡單,更安全和內存泄漏的零風險。

2

從Qt文檔

構造具有給定寬度,高度和格式,使用 現有存儲器緩衝器,數據的圖像。寬度和高度必須爲 (以像素爲單位),數據必須是32位對齊的,並且圖像中的每條掃描線的數據必須也是32位對齊的。

緩衝區必須在整個的QImage的生活仍然有效。破壞時, 圖像不會刪除緩衝區。

你需要實現自己的方式來刪除緩衝

+0

是的,我知道圖像不會破壞''數據''​​。但我不知道如何在我給出的特定示例中實現這一點! – mimo

2

所以,你需要刪除data自己,但當然,訣竅是不要太快刪除它 - 尤其是你不想要在QImage對象可能仍在使用它時刪除它。確保你的情況最簡單的方法是將其刪除後,才QImage的對象已被破壞:

void updateDisplay() { 
    uchar *data = new ...; // array of pixel data 
    ... 
    { 
     QImage image = QImage(data, width, height, 
         width, QImage::Format_Indexed8); 

     pixmapItem->setPixmap(QPixmap::fromImage(image)); 
    } 
    delete [] data; 
} 

(注意,使用大括號創建一個內部範圍,從而確保QImage的對象獲取出棧和的delete [] data行執行之前被銷燬!)

當然,更簡單,更安全的方法是避免在對刪除的問題完全由從來沒有手動擺在首位的陣列分配。取而代之的是,讓QImage的對象分配自己的數據陣列和只寫成代替:

QImage image(width, height, QImage::Format_Indexed8); 
char * data = image.bits(); 
// write into (data) here if you want to, but don't delete [] data, ever! 
// instead, the QImage destructor will do any necessary deletes for you 
1

你也可以繼承QImage用自己的析構函數,做了清理。

class NativeBufferQImage : public QImage { 
    uchar *data; 

    // other stuff 

public: 
    ~NativeBufferQImage() { 
     delete[] this->data; 
    } 
}; 

還要注意,由於QImageQPaintDeviceQPaintDevice衍生擁有virtual destructor,所以如果有人deleteNativeBufferQImage通過一個指向它的基類(即QImage),你的析構函數也將被調用。

+0

'QImage'不是從'QObject'派生的。它從'QPaintDevice'繼承。但虛擬析構函數仍然存在。 – thuga

+0

@thuga哦......謝謝你的注意。將編輯我的答案。 –

+0

這可以消除使用共享數據的圖像或使用QImage對象具有更長使用壽命的數據的優勢。子類QImage使用智能指針?什麼是具體的用例,當你必須使用獨立的數據緩衝區來構建QImage? – Swift

1

做到這一點的最好辦法是不要做:

void updateDisplay() { 
    QImage image(width, height, QImage::Format_Indexed8); 
    auto const data = image.bits(); 
    // you can modify the data here 
    pixmapItem->setPixmap(QPixmap::fromImage(image)); 
} 
+0

它並不總是最好的方法.. Qt的內部存儲是一個碎片混亂..從數據緩衝區創建圖像允許使用內存池。 Qt Ofc,沒有人應該圍繞非託管指針XD – Swift

+0

碎片混亂? Qt使用C++運行時提供的內存分配器。在任何理智的平臺上,它應該是一個體面的非分段分配器。問題的提問者表示該領域沒有特別需要。 –

+0

@庫巴奧伯,這相當天真的看法。不,C++運行時不能保證內存碎片不會發生,而且所有對象一旦分配,都是靜態的,因此碎片整理是不可能的。作爲開發人員嵌入式軟件,在不允許任何分割場景的情況下,我知道Qt甚至不符合「體面」級別的碎片管理,因爲大多數Qt對象都是不可移動的。這是他們自己實現容器的原因之一,比如QList。圖像可能是大量數據,有時可能是千兆字節,這可能會迅速導致堆空間變差 – Swift