Qt 智能指針學習(7種指針)

Qt 智能指針學習html

轉載自:http://blog.csdn.net/dbzhang800/article/details/6403285node

 

從內存泄露開始?

很簡單的入門程序,應該比較熟悉吧 ^_^程序員

#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 說開來 一文中,咱們提到這個程序存在內存泄露(表現就是析構函數不被調用),並且當時給出了三種解決方法:數組

  • 將label對象分配到stack而不是heap中
  • 給label設置標記位Qt::WA_DeleteOnClose安全

  • 本身調用delete來刪除經過new分配到heap中的 label 對象

注:app

  • 爲了能清楚觀察構造和析構函數的調用,咱們能夠簡單子類化了一下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)角度繼續考慮這個問題ide

智能指針

爲了管理內存等資源,C++程序員一般採用RAII(Resource Acquisition Is Initialization)機制:在類的構造函數中申請資源,而後使用,最後在析構函數中釋放資源。svn

若是沒有智能指針,程序員必須保證new對象能在正確的時機delete,四處編寫異常捕獲代碼以釋放資源,而智能指針則能夠在退出做用域時(不論是正常流程離開或是因異常離開)總調用delete來析構在堆上動態分配的對象。函數

咱們看看Qt家族的智能指針:工具

智能指針

 

引入

QPointer

Qt Object 模型的特性(之一)
注意:析構時不會delete它管理的資源

 

QSharedPointer

帶引用計數

Qt4.5

QWeakPointer

 

Qt4.5

QScopedPointer

 

Qt4.6

QScopedArrayPointer

QScopedPointer的派生類

Qt4.6

QSharedDataPointer

用來實現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!"));

根據你所用的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> ,行爲和常規的指針 T * 相似

  • 特色:當其指向的對象(T必須是QObject及其派生類)被銷燬時,它會被自動置NULL.
    • 注意:它自己析構時不會自動銷燬所guarded的對象
  • 用途:當你須要保存其餘人所擁有的QObject對象的指針時,這點很是有用

一個例子

     QPointer<QLabel> label = new QLabel;
     label->setText("&Status:");
     ...
     if (label)
         label->show();

若是在...部分你將該對象delete掉了,label會自動置NULL,而不會是一個懸掛(dangling)的野指針。

QPointer 屬於Qt Object模型的核心機制之一,請注意和其餘智能指針的區別。

std::auto_ptr

這個沒多少要說的。

  • 不能讓多個auto_ptr 指向同一個對象!(auto_ptr被銷燬時會自動刪除它指向的對象,這樣對象會被刪除屢次)
    • 經過拷貝構造或賦值進行操做時,被拷貝的會自動變成NULL,複製所得的指針將得到資源的惟一全部權
  • 智能指針不能指向數組(由於其實現中調用的是delete而非delete[])
  • 智能指針不能做爲容器類的元素。

在C++0x中,auto_ptr已經不建議使用,之後應該會被其餘3個智能指針所取代。

QScopedPointer 與 std::unique_ptr

它們概念上應該是是同樣的。下面再也不區分:

這是一個很相似auto_ptr的智能指針,它包裝了new操做符在堆上分配的動態對象,可以保證動態建立的對象在任什麼時候候均可以被正確地刪除。但它的全部權更加嚴格,不能轉讓,一旦獲取了對象的管理權,你就沒法再從它那裏取回來。

不管是QScopedPointer 仍是 std::unique_ptr 都擁有一個很好的名字,它向代碼的閱讀者傳遞了明確的信息:這個智能指針只能在本做用域裏使用,不但願被轉讓。由於它的拷貝構造和賦值操做都是私有的,這點咱們能夠對比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語句,稍有不慎就會致使資源泄露。採用智能指針後,咱們就能夠將這些異常處理語句簡化了:

 void myFunction(bool useSubClass)
 {
     QScopedPointer<MyClass> p(useSubClass ? new MyClass() : new MySubClass);
     QScopedPointer<QIODevice> device(handsOverOwnership());

     if (m_value > 3)
         return;

     process(device);
 }

另,咱們一開始的例子,也是使用這兩個指針的最佳場合了(出main函數做用域就將其指向的對象銷燬)。

注意:由於拷貝構造和賦值操做私有的,它也具備auto_ptr一樣的「缺陷」——不能用做容器的元素。

QSharedPointer 與 std::shared_ptr

QSharedPointer 與 std::shared_ptr 行爲最接近原始指針,是最像指針的"智能指針",應用範圍比前面的提到的更廣。

QSharedPointer 與 QScopedPointer 同樣包裝了new操做符在堆上分配的動態對象,但它實現的是引用計數型的智能指針 ,能夠被自由地拷貝和賦值,在任意的地方共享它,當沒有代碼使用(引用計數爲0)它時才刪除被包裝的動態分配的對象。shared_ptr也能夠安全地放到標準容器中,並彌補了std::auto_ptr 和 QScopedPointer 由於轉移語義而不能把指針做爲容器元素的缺陷。

QWeakPointer 與 std::weak_ptr

強引用類型的QSharedPointer已經很是好用,爲何還要有弱引用的 QWeakPointer?

QWeakPointer 是爲配合 QSharedPointer 而引入的一種智能指針,它更像是 QSharedPointer 的一個助手(由於它不具備普通指針的行爲,沒有重載operator*和->)。它的最大做用在於協助 QSharedPointer 工做,像一個旁觀者同樣來觀測資源的使用狀況。

  • weak_ptr 主要是爲了不強引用造成環狀。摘自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 實現隱式共享(寫時複製 copy-on-write))而提供的便利工具。

Qt中衆多的類都使用了隱式共享技術,好比QPixmap、QByteArray、QString、...。而咱們爲本身的類實現隱式共享也很簡單,好比要實現一個 Employee類:

  • 定義一個只含有一個數據成員(QSharedDataPointer<EmployeeData>) 的 Employee 類

  • 咱們須要的全部數據成員放置於 派生自QSharedData的 EmployeeData類中。

具體實現看 QSharedDataPointer 的Manual,此處略

QExplicitlySharedDataPointer

這是爲配合 QSharedData 實現顯式共享而提供的便利工具。

QExplicitlySharedDataPointer 和 QSharedDataPointer 很是相似,可是它禁用了寫時複製功能。這使得咱們建立的對象更像一個指針。

一個例子,接前面的Employee:

 #include "employee.h"

 int main()
 {
     Employee e1(1001, "Albrecht Durer");
     Employee e2 = e1;
     e1.setName("Hans Holbein");
 }

寫時複製技術致使:e1和e2有相同的工號,但有不一樣名字。與咱們期待的不一樣,顯式共享能夠解決這個問題,這也使得Employee自己更像一個指針。

補遺

先前竟未注意到官方的這兩篇文章(這是失敗):
便看看google編碼規範中對3個智能指針的建議:
scoped_ptr
Straightforward and risk-free. Use wherever appropriate.
auto_ptr
Confusing and bug-prone ownership-transfer semantics. Do not use.
shared_ptr
Safe with const referents (i.e.  shared_ptr<const T> ). Reference-counted pointers with non-const referents can occasionally be the best design, but try to rewrite with single owners where possible.

參考

 http://blog.csdn.net/wsh6759/article/details/7432200

相關文章
相關標籤/搜索