QScopedPointer

Scope:范围/作用域

明确两个概念:
1、智能指针不是指针
2、智能指针持有目的指针

作用

这个指针的作用如名字一般,在作用域内使用的指针.在变量超出作用域范围时自动调用指针类型的析构函数进行销毁.(这里和栈上分配内存是一致的,实际就是利用栈的特性去管理堆上的内存

构造函数

  1. template <typename T, typename Cleanup = QScopedPointerDeleter<T> >
  2. class QScopedPointer
  3. {
  4. typedef T *QScopedPointer:: *RestrictedBool;
  5. public:
  6. explicit QScopedPointer(T *p = nullptr) Q_DECL_NOTHROW : d(p)
  7. {
  8. }
  9. ...
  10. protected:
  11. T *d;
  12. private:
  13. Q_DISABLE_COPY(QScopedPointer)
  14. };

构造时传入T类型的指针p,并通过成员变量d进行保存(还是不由自主地感叹了下模板真好用^_^)

构造函数比较简单,按照其实现,用法如下:

  1. QScopedPointer<MyClass> mc(new MyClass);
  2. // 和上面的用法相同
  3. QScopedPointer<MyClass, QScopedPointerDeleter> mc(new MyClass);

模板参数中的Cleanup默认使用的是QScopedPointerDeleter,即默认使用delete对指向的内存进行销毁。见删除器小节。

如果仔细一些,会发现构造函数中有如下一行代码,并进行了实现:

  1. typedef T *QScopedPointer:: *RestrictedBool;
  2. #if defined(Q_QDOC)
  3. inline operator bool() const
  4. {
  5. return isNull() ? nullptr : &QScopedPointer::d;
  6. }
  7. #else
  8. operator RestrictedBool() const Q_DECL_NOTHROW
  9. {
  10. return isNull() ? nullptr : &QScopedPointer::d;
  11. }
  12. #endif

这里其实是对隐式转换做了限制,避免用指针做比较时,除了转换成bool类型不能转换其他的类型,例如下面的代码会在编译期就提示你有错误:

  1. QScopedPointer<Foo> foo(nullptr);
  2. int i = 1;
  3. if (foo < i)
  4. ...

但是这样是可以的:

  1. QScopedPointer<Foo> foo(nullptr);
  2. int i = 1;
  3. if (foo)
  4. ...

析构函数

在QScopedPointer超出作用域后,智能指针本身(分配在栈上)自动销毁,触发析构函数。在析构函数中,提供了统一的**清理**函数Cleanup::cleanup(oldD);对oldD(智能指针指向的内存)进行销毁.

  1. inline ~QScopedPointer()
  2. {
  3. T *oldD = this->d;
  4. Cleanup::cleanup(oldD);
  5. }

关于Cleanup的介绍见下一小节.

删除器

QScopedPointer不仅适用于通过new操作符分配的单个内存和一组内存(数组),也可用于通过malloc进行分配的内存。同时,针对QObject的deleteLater特性,也有相关处理。这里要说一下他的Cleanup删除器(非专有名词,只有我这样称呼)的概念。
Cleanup用于超出作用域时自动销毁指针指向的内存,针对不同方式分配的内存,提供了不同的销毁方式:

删除器种类 如何销毁 销毁内存的分配方式
QScopedPointerDeleter delete 使用new分配的内存
QScopedPointerArrayDeleter delet[] 使用new []分配内存
QScopedPointerPodDeleter free 内存由malloc进行分配
QScopedPointerObjectDeleteLater delateLater 当你的指针为QObject类型时,并且处于一个QEventLoop中

你也可以实现自己的删除器,它们需要具有公共静态函数 void cleanup(T *pointer).

  1. // this QScopedPointer deletes its data using the delete[] operator:
  2. QScopedPointer<int, QScopedPointerArrayDeleter<int> > arrayPointer(new int[42]);
  3. // this QScopedPointer frees its data using free():
  4. QScopedPointer<int, QScopedPointerPodDeleter> podPointer(reinterpret_cast<int *>(malloc(42)));
  5. // this struct calls "myCustomDeallocator" to delete the pointer
  6. struct ScopedPointerCustomDeleter
  7. {
  8. static inline void cleanup(MyCustomClass *pointer)
  9. {
  10. myCustomDeallocator(pointer);
  11. }
  12. };
  13. // QScopedPointer using a custom deleter:
  14. QScopedPointer<MyCustomClass, ScopedPointerCustomDeleter> customPointer(new MyCustomClass);

方法一览

原型 接口说明
QScopedPointer(T *p = …) Constructs this QScopedPointer instance and sets its pointer to p.
~QScopedPointer() Destroys this QScopedPointer object. Delete the object its pointer points to.
T * data() const Returns the value of the pointer referenced by this object. QScopedPointer still owns the object pointed to.
T * get() const Same as data().
bool isNull() const Returns true if this object is holding a pointer that is null.
void reset(T *other = …) Deletes the existing object it is pointing to (if any), and sets its pointer to other. QScopedPointer now owns other and will delete it in its destructor. To clear the pointer held without deleting the object it points to (and hence take ownership of the object), use take() instead.
void swap(QScopedPointer &other) Swap this pointer with other.
T * take() Returns the value of the pointer referenced by this object. The pointer of this QScopedPointer object will be reset to null. Callers of this function take ownership of the pointer.
bool operator bool() const Returns true if this object is not null. This function is suitable for use in if-constructs, like:

if (scopedPointer) {

}

See also isNull(). | | bool operator!() const | Returns true if the pointer referenced by this object is null, otherwise returns false.
See also isNull(). | | T & operator() const | Provides access to the scoped pointer’s object.
If the contained pointer is null, behavior is undefined.
See also isNull(). | | T
operator->() const | Provides access to the scoped pointer’s object.
If the contained pointer is null, behavior is undefined.
See also isNull(). |

实际场景

  1. void myFunction(bool useSubClass)
  2. {
  3. MyClass *p = useSubClass ? new MyClass() : new MySubClass;
  4. QIODevice *device = handsOverOwnership();
  5. if (m_value > 3) {
  6. delete p;
  7. delete device;
  8. return;
  9. }
  10. try {
  11. process(device);
  12. }
  13. catch (...) {
  14. delete p;
  15. delete device;
  16. throw;
  17. }
  18. delete p;
  19. delete device;
  20. }

如果使用QScopedPointer,可以这样写

  1. void myFunction(bool useSubClass)
  2. {
  3. // assuming that MyClass has a virtual destructor
  4. QScopedPointer<MyClass> p(useSubClass ? new MyClass() : new MySubClass);
  5. QScopedPointer<QIODevice> device(handsOverOwnership());
  6. if (m_value > 3)
  7. return;
  8. process(device);
  9. }

QScopedPointer intentionally has no copy constructor or assignment operator, such that ownership and lifetime is clearly communicated.
QScopedPointer故意去除了拷贝构造和赋值构造函数,因为指针不能被传递和复制,这样关于指针的所有权和生命周期就很明确(跟随智能指针的销毁而销毁).

如果你的指针需要用const限定符去修饰,使用QScopedPointer后也是支持的

  1. const QWidget *const p = new QWidget();
  2. // is equivalent to:
  3. const QScopedPointer<const QWidget> p(new QWidget());
  4. QWidget *const p = new QWidget();
  5. // is equivalent to:
  6. const QScopedPointer<QWidget> p(new QWidget());
  7. const QWidget *p = new QWidget();
  8. // is equivalent to:
  9. QScopedPointer<const QWidget> p(new QWidget());

QScopedArrayPointer

和QScopedPointer不同的是,他的删除器默认是QScopedPointerArrayDeleter,其他都差不多,我们了解即可

  1. template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> >
  2. class QScopedArrayPointer : public QScopedPointer<T, Cleanup>
  3. {
  4. template <typename Ptr>
  5. using if_same_type = typename std::enable_if<std::is_same<typename std::remove_cv<T>::type, Ptr>::value, bool>::type;
  6. public:
  7. inline QScopedArrayPointer() : QScopedPointer<T, Cleanup>(nullptr) {}
  8. template <typename D, if_same_type<D> = true>
  9. explicit QScopedArrayPointer(D *p)
  10. : QScopedPointer<T, Cleanup>(p)
  11. {
  12. }

源码赏析

  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2016 The Qt Company Ltd.
  4. ** Contact: https://www.qt.io/licensing/
  5. **
  6. ** This file is part of the QtCore module of the Qt Toolkit.
  7. **
  8. ** $QT_BEGIN_LICENSE:LGPL$
  9. ** Commercial License Usage
  10. ** Licensees holding valid commercial Qt licenses may use this file in
  11. ** accordance with the commercial license agreement provided with the
  12. ** Software or, alternatively, in accordance with the terms contained in
  13. ** a written agreement between you and The Qt Company. For licensing terms
  14. ** and conditions see https://www.qt.io/terms-conditions. For further
  15. ** information use the contact form at https://www.qt.io/contact-us.
  16. **
  17. ** GNU Lesser General Public License Usage
  18. ** Alternatively, this file may be used under the terms of the GNU Lesser
  19. ** General Public License version 3 as published by the Free Software
  20. ** Foundation and appearing in the file LICENSE.LGPL3 included in the
  21. ** packaging of this file. Please review the following information to
  22. ** ensure the GNU Lesser General Public License version 3 requirements
  23. ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
  24. **
  25. ** GNU General Public License Usage
  26. ** Alternatively, this file may be used under the terms of the GNU
  27. ** General Public License version 2.0 or (at your option) the GNU General
  28. ** Public license version 3 or any later version approved by the KDE Free
  29. ** Qt Foundation. The licenses are as published by the Free Software
  30. ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
  31. ** included in the packaging of this file. Please review the following
  32. ** information to ensure the GNU General Public License requirements will
  33. ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
  34. ** https://www.gnu.org/licenses/gpl-3.0.html.
  35. **
  36. ** $QT_END_LICENSE$
  37. **
  38. ****************************************************************************/
  39. #ifndef QSCOPEDPOINTER_H
  40. #define QSCOPEDPOINTER_H
  41. #include <QtCore/qglobal.h>
  42. #include <stdlib.h>
  43. QT_BEGIN_NAMESPACE
  44. template <typename T>
  45. struct QScopedPointerDeleter
  46. {
  47. static inline void cleanup(T *pointer)
  48. {
  49. // Enforce a complete type.
  50. // If you get a compile error here, read the section on forward declared
  51. // classes in the QScopedPointer documentation.
  52. typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ];
  53. (void) sizeof(IsIncompleteType);
  54. delete pointer;
  55. }
  56. };
  57. template <typename T>
  58. struct QScopedPointerArrayDeleter
  59. {
  60. static inline void cleanup(T *pointer)
  61. {
  62. // Enforce a complete type.
  63. // If you get a compile error here, read the section on forward declared
  64. // classes in the QScopedPointer documentation.
  65. typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ];
  66. (void) sizeof(IsIncompleteType);
  67. delete [] pointer;
  68. }
  69. };
  70. struct QScopedPointerPodDeleter
  71. {
  72. static inline void cleanup(void *pointer) { if (pointer) free(pointer); }
  73. };
  74. #ifndef QT_NO_QOBJECT
  75. template <typename T>
  76. struct QScopedPointerObjectDeleteLater
  77. {
  78. static inline void cleanup(T *pointer) { if (pointer) pointer->deleteLater(); }
  79. };
  80. class QObject;
  81. typedef QScopedPointerObjectDeleteLater<QObject> QScopedPointerDeleteLater;
  82. #endif
  83. template <typename T, typename Cleanup = QScopedPointerDeleter<T> >
  84. class QScopedPointer
  85. {
  86. typedef T *QScopedPointer:: *RestrictedBool;
  87. public:
  88. explicit QScopedPointer(T *p = nullptr) Q_DECL_NOTHROW : d(p)
  89. {
  90. }
  91. inline ~QScopedPointer()
  92. {
  93. T *oldD = this->d;
  94. Cleanup::cleanup(oldD);
  95. }
  96. inline T &operator*() const
  97. {
  98. Q_ASSERT(d);
  99. return *d;
  100. }
  101. T *operator->() const Q_DECL_NOTHROW
  102. {
  103. return d;
  104. }
  105. bool operator!() const Q_DECL_NOTHROW
  106. {
  107. return !d;
  108. }
  109. #if defined(Q_QDOC)
  110. inline operator bool() const
  111. {
  112. return isNull() ? nullptr : &QScopedPointer::d;
  113. }
  114. #else
  115. operator RestrictedBool() const Q_DECL_NOTHROW
  116. {
  117. return isNull() ? nullptr : &QScopedPointer::d;
  118. }
  119. #endif
  120. T *data() const Q_DECL_NOTHROW
  121. {
  122. return d;
  123. }
  124. T *get() const Q_DECL_NOTHROW
  125. {
  126. return d;
  127. }
  128. bool isNull() const Q_DECL_NOTHROW
  129. {
  130. return !d;
  131. }
  132. void reset(T *other = nullptr) Q_DECL_NOEXCEPT_EXPR(noexcept(Cleanup::cleanup(std::declval<T *>())))
  133. {
  134. if (d == other)
  135. return;
  136. T *oldD = d;
  137. d = other;
  138. Cleanup::cleanup(oldD);
  139. }
  140. T *take() Q_DECL_NOTHROW
  141. {
  142. T *oldD = d;
  143. d = nullptr;
  144. return oldD;
  145. }
  146. void swap(QScopedPointer<T, Cleanup> &other) Q_DECL_NOTHROW
  147. {
  148. qSwap(d, other.d);
  149. }
  150. typedef T *pointer;
  151. protected:
  152. T *d;
  153. private:
  154. Q_DISABLE_COPY(QScopedPointer)
  155. };
  156. template <class T, class Cleanup>
  157. inline bool operator==(const QScopedPointer<T, Cleanup> &lhs, const QScopedPointer<T, Cleanup> &rhs) Q_DECL_NOTHROW
  158. {
  159. return lhs.data() == rhs.data();
  160. }
  161. template <class T, class Cleanup>
  162. inline bool operator!=(const QScopedPointer<T, Cleanup> &lhs, const QScopedPointer<T, Cleanup> &rhs) Q_DECL_NOTHROW
  163. {
  164. return lhs.data() != rhs.data();
  165. }
  166. template <class T, class Cleanup>
  167. inline bool operator==(const QScopedPointer<T, Cleanup> &lhs, std::nullptr_t) Q_DECL_NOTHROW
  168. {
  169. return lhs.isNull();
  170. }
  171. template <class T, class Cleanup>
  172. inline bool operator==(std::nullptr_t, const QScopedPointer<T, Cleanup> &rhs) Q_DECL_NOTHROW
  173. {
  174. return rhs.isNull();
  175. }
  176. template <class T, class Cleanup>
  177. inline bool operator!=(const QScopedPointer<T, Cleanup> &lhs, std::nullptr_t) Q_DECL_NOTHROW
  178. {
  179. return !lhs.isNull();
  180. }
  181. template <class T, class Cleanup>
  182. inline bool operator!=(std::nullptr_t, const QScopedPointer<T, Cleanup> &rhs) Q_DECL_NOTHROW
  183. {
  184. return !rhs.isNull();
  185. }
  186. template <class T, class Cleanup>
  187. inline void swap(QScopedPointer<T, Cleanup> &p1, QScopedPointer<T, Cleanup> &p2) Q_DECL_NOTHROW
  188. { p1.swap(p2); }
  189. template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> >
  190. class QScopedArrayPointer : public QScopedPointer<T, Cleanup>
  191. {
  192. template <typename Ptr>
  193. using if_same_type = typename std::enable_if<std::is_same<typename std::remove_cv<T>::type, Ptr>::value, bool>::type;
  194. public:
  195. inline QScopedArrayPointer() : QScopedPointer<T, Cleanup>(nullptr) {}
  196. template <typename D, if_same_type<D> = true>
  197. explicit QScopedArrayPointer(D *p)
  198. : QScopedPointer<T, Cleanup>(p)
  199. {
  200. }
  201. inline T &operator[](int i)
  202. {
  203. return this->d[i];
  204. }
  205. inline const T &operator[](int i) const
  206. {
  207. return this->d[i];
  208. }
  209. void swap(QScopedArrayPointer &other) Q_DECL_NOTHROW // prevent QScopedPointer <->QScopedArrayPointer swaps
  210. { QScopedPointer<T, Cleanup>::swap(other); }
  211. private:
  212. explicit inline QScopedArrayPointer(void *) {
  213. // Enforce the same type.
  214. // If you get a compile error here, make sure you declare
  215. // QScopedArrayPointer with the same template type as you pass to the
  216. // constructor. See also the QScopedPointer documentation.
  217. // Storing a scalar array as a pointer to a different type is not
  218. // allowed and results in undefined behavior.
  219. }
  220. Q_DISABLE_COPY(QScopedArrayPointer)
  221. };
  222. template <typename T, typename Cleanup>
  223. inline void swap(QScopedArrayPointer<T, Cleanup> &lhs, QScopedArrayPointer<T, Cleanup> &rhs) Q_DECL_NOTHROW
  224. { lhs.swap(rhs); }
  225. QT_END_NAMESPACE
  226. #endif // QSCOPEDPOINTER_H

附录

本文资料为以下链接的总结,可能大量借鉴其中内容,因此不敢说原创,仅做分享之用,如有侵权,告知必删。

QScopedPointer Class