1. 程式人生 > >Qt中的智慧指標

Qt中的智慧指標

儘管C++不支援垃圾回收,但C++物件的自動記憶體管理還是可以通過好幾種方式來實現,主要是通過智慧指標使用以及引用計數。Qt提供了許多不同的智慧指標型別,以適用於不同的用途。

一個重寫了指標解引用操作operator*()和operator->()的類被稱為智慧指標。這使得類例項的行為就像它是一個內建指標一樣。這樣的類幾乎總是模板類,因此定義時必須在模板引數中提供引用型別。最常見的能找到這些重寫操作運算元的地方是在迭代器以及智慧指標中。使它們變得智慧的通常是在構造、析構以及賦值中的自定義行為。

QScopedPointer是一個在指標作用於結束後自動刪除所引用物件的智慧指標。它類似於std::auto_ptr。複製QScopedPointer是毫無意義的,因為它會導致所引用的物件重複刪除。指標的作用域指標的作用域明確地表明瞭所引用物件的生存期和所屬。

類似於QScopedPointer,QSharedPointer是一個自動刪除它所引用的物件的智慧指標,但是它允許被複制,而且QSharedPointer會保持一個引用計數。共享的堆物件只有在最後一個指向它的智慧指標銷燬時才會被刪除。

一、QScopedPointer

QScopedPointer類儲存了一個指向動態分配物件的指標,一旦析構就會被刪除。

手動管理對分配的物件是困難的並且容易出錯的,通常會導致記憶體洩漏並且難於維護。QScopedPointer是一個小型工具類,它通過賦予基於棧記憶體的所有權給堆分配,大大簡化了上述操作,更一般的被稱為資源獲取時初始化(RAII)。

QScopedPointer保證超出當前作用域時指向的物件會被刪除。

考慮這個執行堆分配、有不同出口點的函式:

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呼叫簡直就是累贅。使用QScopedPointer,程式碼可以簡化為:
void myFunction(bool useSubClass)
{
    // assuming that MyClass has a virtual destructor
    QScopedPointer<MyClass> p(useSubClass ? new MyClass() : new MySubClass);
    QScopedPointer<QIODevice> device(handsOverOwnership());

    if (m_value > 3)
        return;

    process(device);
}

QScopedPointer有意地沒有實現拷貝建構函式和賦值操作符,這樣所屬和生命週期才相通。

在常規C++指標上的常量限制也可以使用QScopedPointer表示:

    const QWidget *const p = new QWidget();
    // is equivalent to:
    const QScopedPointer<const QWidget> p(new QWidget());


    QWidget *const p = new QWidget();
    // is equivalent to:
    const QScopedPointer<QWidget> p(new QWidget());


    const QWidget *p = new QWidget();
    // is equivalent to:
    QScopedPointer<const QWidget> p(new QWidget());


自定義清理處理函式

陣列以及使用malloc分配的指標一定不能使用delete刪除。

QScopedPointer的第二個模板引數可以用於自定義清理處理函式。

存在以下自定義清理處理函式:

1)QScopedPointerDeleter——預設,使用delete刪除指標。

2)QScopedPointerArrayDeleter——使用delete []刪除指標。這個處理函式用於使用new []分配的指標。

3)QScopedPointerPodDeleter——使用free()刪除指標。這個處理函式用於使用malloc()分配的指標。

4)QScopedPointerDeleteLater——通過呼叫deleteLater()刪除指標。這個處理函式用於指向活躍於QLoopEvent中的QObject物件的指標。

你可以傳遞自己的類作為處理函式,只要這些類有公有的靜態函式 void cleanup(T* pointer)。

// this QScopedPointer deletes its data using the delete[] operator:
QScopedPointer<int, QScopedPointerArrayDeleter<int> > arrayPointer(new int[42]);

// this QScopedPointer frees its data using free():
QScopedPointer<int, QScopedPointerPodDeleter> podPointer(reinterpret_cast<int *>(malloc(42)));

// this struct calls "myCustomDeallocator" to delete the pointer
struct ScopedPointerCustomDeleter
{
    static inline void cleanup(MyCustomClass *pointer)<span style="white-space:pre">	</span>// 這個不是公有的吧
    {
        myCustomDeallocator(pointer);
    }
};

// QScopedPointer using a custom deleter:
QScopedPointer<MyCustomClass, ScopedPointerCustomDeleter> customPointer(new MyCustomClass);
前向宣告指標

前向宣告的類可以用於QScopedPointer,只要當QScopedPointer需要清理時前向宣告類的解構函式可用就行。

具體來說,這意味著所有包含指向一個前向宣告類的QScopedPointer類的那些類必須有非內聯的建構函式、解構函式和賦值運算子:

class MyPrivateClass; // forward declare MyPrivateClass

class MyClass
{
private:
    QScopedPointer<MyPrivateClass> privatePtr; // QScopedPointer to forward declared class

public:
    MyClass(); // OK
    inline ~MyClass() {} // VIOLATION - Destructor must not be inline

private:
    Q_DISABLE_COPY(MyClass) // OK - copy constructor and assignment operators
                             // are now disabled, so the compiler won't implicitely
                             // generate them.
};
否則,編譯器會輸出”無法析構MyPrivateClass“的警告。