概述
QScopedPointer和C++中的智能指針std::unique_ptr其概念是一樣的,它包裝了new操作符在堆上分配的動態對象,能夠保證動態創建的對象在任何時候都可以被正確地刪除。但它有更嚴格的所有權,並且不能轉讓,一旦獲取了對象的管理權,你就無法再從它那里取回來。也就是說,只要出了作用域,指針就會被自動刪除,因為它的拷貝構造和賦值操作都是私有的,與QObject及其派生類風格相同。
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; }
上面的寫法,稍有不慎就會導致內存泄露,但是如果使用智能指針,就會變得很簡單了:
void myFunction(bool useSubClass) { QScopedPointer<MyClass> p(useSubClass ? new MyClass() : new MySubClass); QScopedPointer<QIODevice> device(handsOverOwnership()); if (m_value > 3) return; process(device); }
注意:因為拷貝構造和賦值操作私有的,所以不能用作容器的元素。
const 限制
C ++指針的const限定也可以用QScopedPointer表示:
const QWidget *const p = new QWidget(); // 等同於: const QScopedPointer<const QWidget> p(new QWidget()); QWidget *const p = new QWidget(); // 等同於: const QScopedPointer<QWidget> p(new QWidget()); const QWidget *p = new QWidget(); // 等同於: QScopedPointer<const QWidget> p(new QWidget());
考慮一種情況
上面說到,使用QScopedPointer智能指針動態創建的對象,一旦出了作用域就會 被自動釋放並置空,那么如果需要函數返回值怎么辦呢?
比如下面這種情況:
QLabel * createLabel() { QScopedPointer<QLabel> pLabel(new QLabel()); // return pLabel.data(); //invalid return pLabel.take(); //valid } int main(int argc, char *argv[]) { QApplication a(argc, argv); QScopedPointer<QLabel> p1(createLabel()); p1->setText("hello"); p1->show(); return a.exec(); }
注意,我們在createLabel()函數中創建label對象並返回時,不能使用data(),而要使用take();
因為 T *QScopedPointer::data() const返回指向對象的常量指針,QScopedPointer仍擁有對象所有權。 所以通過data()返回過后就被自動刪除了,從而導致mian函數中的p1變成了野指針,程序崩潰。
而使用T *QScopedPointer::take()也是返回對象指針,但QScopedPointer不再擁有對象所有權,而是轉移到調用這個函數的caller,同時QScopePointer對象指針置為NULL。
另外還有一個函數要注意。
void QScopedPointer::reset(T *other = Q_NULLPTR):delete目前指向的對象,調用其析構函數,將指針指向另一個對象other,所有權轉移到other。
QScopedArrayPointer
對應的還有一個指針QScopedArrayPointer,專門用於處理數組,其用法和QScopedPointer是一樣的
官方簡單示例:
void foo() { QScopedArrayPointer<int> i(new int[10]); i[2] = 42; ... return; // our integer array is now deleted using delete[] }
超出作用域過后會自動調用delete[]刪除指針,這里就不展開描述了。