Qt 智能指针学习
原地址:http://blog.csdn.net/dbzhang800/article/details/6403285
從內(nèi)存泄露開始?
很簡單的入門程序,應(yīng)該比較熟悉吧 ^_^
#include <QApplication> #include <QLabel>int main(int argc, char *argv[]) {QApplication app(argc, argv);QLabel *label = new QLabel("Hello Dbzhang800!");label->show();return app.exec(); }在??從 Qt 的 delete 說開來?一文中,我們提到這個程序存在內(nèi)存泄露(表現(xiàn)就是析構(gòu)函數(shù)不被調(diào)用),而且當時給出了三種解決方法:
- 將label對象分配到stack而不是heap中
-
給label設(shè)置標記位Qt::WA_DeleteOnClose
- 自己調(diào)用delete來刪除通過new分配到heap中的 label 對象
注:
- 為了能清楚觀察構(gòu)造和析構(gòu)函數(shù)的調(diào)用,我們可以簡單子類化了一下QLabel,然后用Label取代前面的QLabel class Label :public QLabel { public:Label(const QString& text, QWidget *parent=NULL):QLabel(text, parent){qDebug("from constructor");}~Label(){qDebug("from destructor");} };
本文中,我們從智能指針(smart pointer)角度繼續(xù)考慮這個問題
智能指針
為了管理內(nèi)存等資源,C++程序員通常采用RAII(Resource Acquisition Is Initialization)機制:在類的構(gòu)造函數(shù)中申請資源,然后使用,最后在析構(gòu)函數(shù)中釋放資源。
如果沒有智能指針,程序員必須保證new對象能在正確的時機delete,四處編寫異常捕獲代碼以釋放資源,而智能指針則可以在退出作用域時(不管是正常流程離開或是因異常離開)總調(diào)用delete來析構(gòu)在堆上動態(tài)分配的對象。
我們看看Qt家族的智能指針:
| 智能指針 | ? | 引入 |
| QPointer | Qt Object 模型的特性(之一) | ? |
| QSharedPointer | 帶引用計數(shù) | Qt4.5 |
| QWeakPointer | ? | Qt4.5 |
| QScopedPointer | ? | Qt4.6 |
| QScopedArrayPointer | QScopedPointer的派生類 | Qt4.6 |
| QSharedDataPointer | 用來實現(xiàn)Qt的隱式共享(Implicit Sharing) | Qt4.0 |
| QExplicitlySharedDataPointer | 顯式共享 | Qt4.4 |
| ? | ? | ? |
| std::auto_ptr | ? | ? |
| std::shared_ptr | std::tr1::shared_ptr | C++0x |
| std::weak_ptr | std::tr1::weak_ptr | C++0x |
| std::unique_ptr | boost::scoped_ptr | C++0x |
注:
- MSVC2010 和 GCC g++ 4.3 支持 C++0x
- MSVC2008 sp1 及 GCC g++ 4.0 支持 tr1
有了這些東西,我們就可以很容易改造我們前面的例子了(只需改變一行):
std::auto_ptr<QLabel> label(new QLabel("Hello Dbzhang800!"));根據(jù)你所用的Qt的版本,以及C++編譯器的支持程度,你可以選用:
- QScopedPointer
- std::unique_ptr
- QSharedPointer
- std::shared_ptr
- std::tr1::shared_ptr
QPointer
如何翻譯呢?我不太清楚,保留英文吧。
-
The QPointer class is a template class that provides??guarded pointers???to QObjects.
-
使用:一個guarded指針,QPointer<T> ,行為和常規(guī)的指針 T * 類似
- 特點:當其指向的對象(T必須是QObject及其派生類)被銷毀時,它會被自動置NULL.
- 注意:它本身析構(gòu)時不會自動銷毀所guarded的對象
- 用途:當你需要保存其他人所擁有的QObject對象的指針時,這點非常有用
一個例子
QPointer<QLabel> label = new QLabel;label->setText("&Status:");...if (label)label->show();如果在...部分你將該對象delete掉了,label會自動置NULL,而不會是一個懸掛(dangling)的野指針。
QPointer 屬于Qt Object模型的核心機制之一,請注意和其他智能指針的區(qū)別。
std::auto_ptr
這個沒多少要說的。
- 不能讓多個auto_ptr 指向同一個對象!(auto_ptr被銷毀時會自動刪除它指向的對象,這樣對象會被刪除多次)
- 通過拷貝構(gòu)造或賦值進行操作時,被拷貝的會自動變成NULL,復(fù)制所得的指針將獲得資源的唯一所有權(quán)
- 智能指針不能指向數(shù)組(因為其實現(xiàn)中調(diào)用的是delete而非delete[])
- 智能指針不能作為容器類的元素。
在C++0x中,auto_ptr已經(jīng)不建議使用,以后應(yīng)該會被其他3個智能指針所取代。
QScopedPointer 與 std::unique_ptr
它們概念上應(yīng)該是是一樣的。下面不再區(qū)分:
這是一個很類似auto_ptr的智能指針,它包裝了new操作符在堆上分配的動態(tài)對象,能夠保證動態(tài)創(chuàng)建的對象在任何時候都可以被正確地刪除。但它的所有權(quán)更加嚴格,不能轉(zhuǎn)讓,一旦獲取了對象的管理權(quán),你就無法再從它那里取回來。
無論是QScopedPointer 還是 std::unique_ptr 都擁有一個很好的名字,它向代碼的閱讀者傳遞了明確的信息:這個智能指針只能在本作用域里使用,不希望被轉(zhuǎn)讓。因為它的拷貝構(gòu)造和賦值操作都是私有的,這點我們可以對比QObject及其派生類的對象哈。
用法?(來自Qt的manual):
考慮沒有智能指針的情況,
void myFunction(bool useSubClass){MyClass *p = useSubClass ? new MyClass() : new MySubClass;QIODevice *device = handsOverOwnership();if (m_value > 3) {delete p;delete device;return;}try {process(device);}catch (...) {delete p;delete device;throw;}delete p;delete device;}我們在異常處理語句中多次書寫delete語句,稍有不慎就會導(dǎo)致資源泄露。采用智能指針后,我們就可以將這些異常處理語句簡化了:
void myFunction(bool useSubClass){QScopedPointer<MyClass> p(useSubClass ? new MyClass() : new MySubClass);QScopedPointer<QIODevice> device(handsOverOwnership());if (m_value > 3)return;process(device);}另,我們一開始的例子,也是使用這兩個指針的最佳場合了(出main函數(shù)作用域就將其指向的對象銷毀)。
注意:因為拷貝構(gòu)造和賦值操作私有的,它也具有auto_ptr同樣的“缺陷”——不能用作容器的元素。
QSharedPointer 與 std::shared_ptr
QSharedPointer 與 std::shared_ptr 行為最接近原始指針,是最像指針的"智能指針",應(yīng)用范圍比前面的提到的更廣。
QSharedPointer 與 QScopedPointer 一樣包裝了new操作符在堆上分配的動態(tài)對象,但它實現(xiàn)的是引用計數(shù)型的智能指針 ,可以被自由地拷貝和賦值,在任意的地方共享它,當沒有代碼使用(引用計數(shù)為0)它時才刪除被包裝的動態(tài)分配的對象。shared_ptr也可以安全地放到標準容器中,并彌補了std::auto_ptr 和 QScopedPointer 因為轉(zhuǎn)移語義而不能把指針作為容器元素的缺陷。
QWeakPointer 與 std::weak_ptr
強引用類型的QSharedPointer已經(jīng)非常好用,為什么還要有弱引用的 QWeakPointer?
QWeakPointer 是為配合 QSharedPointer 而引入的一種智能指針,它更像是 QSharedPointer 的一個助手(因為它不具有普通指針的行為,沒有重載operator*和->)。它的最大作用在于協(xié)助 QSharedPointer 工作,像一個旁觀者一樣來觀測資源的使用情況。
- weak_ptr 主要是為了避免強引用形成環(huán)狀。摘自msdn中一段話:
- A cycle occurs when two or more resources controlled by shared_ptr objects hold mutually referencing shared_ptr objects. For example, a circular linked list with three elements has a head node N0; that node holds a shared_ptr object that owns the next node, N1; that node holds a shared_ptr object that owns the next node, N2; that node, in turn, holds a shared_ptr object that owns the head node, N0, closing the cycle. In this situation, none of the reference counts will ever become zero, and the nodes in the cycle will not be freed. To eliminate the cycle, the last node N2 should hold a weak_ptr object pointing to N0 instead of a shared_ptr object. Since the weak_ptr object does not own N0 it doesn't affect N0's reference count, and when the program's last reference to the head node is destroyed the nodes in the list will also be destroyed.
- 在Qt中,對于QObject及其派生類對象,QWeakPointer有特殊處理。它可以作為QPointer的替代品
- 這種情況下,不需要QSharedPointer的存在
- 效率比 QPointer 高
QSharedDataPointer
這是為配合 QSharedData 實現(xiàn)隱式共享(寫時復(fù)制 copy-on-write))而提供的便利工具。
Qt中眾多的類都使用了隱式共享技術(shù),比如QPixmap、QByteArray、QString、...。而我們?yōu)樽约旱念悓崿F(xiàn)隱式共享也很簡單,比如要實現(xiàn)一個 Employee類:
-
定義一個只含有一個數(shù)據(jù)成員(QSharedDataPointer<EmployeeData>) 的 Employee 類
-
我們需要的所有數(shù)據(jù)成員放置于 派生自QSharedData的 EmployeeData類中。
具體實現(xiàn)看 QSharedDataPointer 的Manual,此處略
QExplicitlySharedDataPointer
這是為配合 QSharedData 實現(xiàn)顯式共享而提供的便利工具。
QExplicitlySharedDataPointer 和 QSharedDataPointer 非常類似,但是它禁用了寫時復(fù)制功能。這使得我們創(chuàng)建的對象更像一個指針。
一個例子,接前面的Employee:
#include "employee.h"int main(){Employee e1(1001, "Albrecht Durer");Employee e2 = e1;e1.setName("Hans Holbein");}寫時復(fù)制技術(shù)導(dǎo)致:e1和e2有相同的工號,但有不同名字。與我們期待的不同,顯式共享可以解決這個問題,這也使得Employee本身更像一個指針。
補遺
先前竟未注意到官方的這兩篇文章(這是失敗):- http://labs.qt.nokia.com/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have/
- http://labs.qt.nokia.com/2009/08/21/introducing-qscopedpointer/
參考
- Qt manual
-
Smart Pointers FAQ
-
The New C++:Smart(er) Pointers
-
http://en.wikipedia.org/wiki/Smart_pointer
-
C++:智能指針-TR1的shared_ptr和weak_ptr使用介紹
-
【C++Boost】智能指針的標準之爭:Boost vs. Loki
-
http://www2.research.att.com/~bs/C++0xFAQ.html
-
http://msdn.microsoft.com/zh-cn/library/bb982126.aspx
-
http://www.codesynthesis.com/~boris/blog/2010/05/24/smart-pointers-in-boost-tr1-cxx-x0/
- http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
轉(zhuǎn)載于:https://www.cnblogs.com/lanye/p/3564448.html
總結(jié)
- 上一篇: volatile关键字及编译器指令乱序总
- 下一篇: ceSetThreadPriority设