QWeakPointer

先介绍QWeakPointer,是因为QPointer和QSharedPointer的实现都依赖于QWeakPointer

作用

1、为QPointer和QSharedPointer提供了弱引用计数的功能.
2、解除循环引用

QWeakPointer 类持有对共享指针的弱引用。
QWeakPointer 是对 C++ 中指针的自动弱引用。它不能用于直接取消引用指针,但可以用于验证指针是否已在另一个上下文中被删除
QWeakPointer 对象只能通过 QSharedPointer 的赋值来创建(因为没有重载operator*和->)。
需要注意的是,QWeakPointer 没有提供自动转换操作符来防止错误发生。即使 QWeakPointer 跟踪一个指针,它本身也不应该被认为是一个指针,因为它不能保证指向的对象保持有效。
因此,要访问 QWeakPointer 正在跟踪的指针,您必须首先将其提升为 QSharedPointer 并验证结果对象是否为空。 QSharedPointer 保证对象不被删除,所以如果你得到一个非空对象,你可以使用指针。有关示例,请参见 QWeakPointer::toStrongRef()。
QWeakPointer 还提供了 QWeakPointer::data() 方法,该方法返回跟踪的指针而不确保它保持有效。如果您可以通过外部方式保证对象不会被删除(或者如果您只需要指针值)那你就可以使用它。友情提示:使用 toStrongRef() 创建 QSharedPointer 的成本较高。

构造函数

观察QWeakPointer的构造函数部分,处于public部分的构造函数有如下几个,可以看出都是不允许带参构造或者参数并不是我们期望的指针类型.(所以,QWeakPointer从设计上就不是直接提供给我们使用的)

  1. // 无参构造
  2. inline QWeakPointer() Q_DECL_NOTHROW : d(nullptr), value(nullptr) { }
  3. // 拷贝构造,弱引用计数+1
  4. QWeakPointer(const QWeakPointer &other) Q_DECL_NOTHROW : d(other.d), value(other.value)
  5. { if (d) d->weakref.ref(); }
  6. // 拷贝构造,弱引用计数+1
  7. inline QWeakPointer(const QSharedPointer<T> &o) : d(o.d), value(o.data())
  8. { if (d) d->weakref.ref();}
  9. template <class X>
  10. inline QWeakPointer(const QWeakPointer<X> &o) : d(nullptr), value(nullptr)
  11. { *this = o; }
  12. template <class X>
  13. inline QWeakPointer(const QSharedPointer<X> &o) : d(nullptr), value(nullptr)
  14. { *this = o; }

唯一的可用的构造函数处于private部分,好处是这里添加了两个友元类型QPointer和QSharedPointer,关于友元我们应该不用多做介绍(我是你的朋友,你的就是我的,但你不是我的朋友,所以我的还是我的^_^)

  1. private:
  2. #if defined(Q_NO_TEMPLATE_FRIENDS)
  3. public:
  4. #else
  5. template <class X> friend class QSharedPointer;
  6. template <class X> friend class QPointer;
  7. #endif
  8. template <class X>
  9. inline QWeakPointer &assign(X *ptr)
  10. { return *this = QWeakPointer<X>(ptr, true); }
  11. #ifndef QT_NO_QOBJECT
  12. // 私有构造函数
  13. template <class X>
  14. inline QWeakPointer(X *ptr, bool) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr)
  15. { }
  16. #endif
  17. inline void internalSet(Data *o, T *actual)
  18. {
  19. if (d == o) return;
  20. if (o)
  21. o->weakref.ref();
  22. if (d && !d->weakref.deref())
  23. delete d;
  24. d = o;
  25. value = actual;
  26. }
  27. // 唯二的成员变量
  28. Data *d;
  29. T *value;
  30. };

QWeakPointer的数据成员只有两个,分别用于在私有构造函数中,ptr为模板参数类型的指针,d=ptr ? Data::getAndRef(ptr) : nullptr;

  1. typedef QtSharedPointer::ExternalRefCountData Data;

看下ExternalRefCountData的定义,从名字大概看出来这是一个关于引用计数的结构,用于保存引用计数相关的数据。
注意下这里的weakref和strongref都是QBasicAtomicInt类型的,关于QAtom系列的几个类,有时间会单独列一章。

  1. struct ExternalRefCountData
  2. {
  3. typedef void (*DestroyerFn)(ExternalRefCountData *);
  4. QBasicAtomicInt weakref;
  5. QBasicAtomicInt strongref;
  6. DestroyerFn destroyer;
  7. inline ExternalRefCountData(DestroyerFn d)
  8. : destroyer(d)
  9. {
  10. strongref.store(1);
  11. weakref.store(1);
  12. }
  13. inline ExternalRefCountData(Qt::Initialization) { }
  14. ~ExternalRefCountData() { Q_ASSERT(!weakref.load()); Q_ASSERT(strongref.load() <= 0); }
  15. void destroy() { destroyer(this); }
  16. #ifndef QT_NO_QOBJECT
  17. Q_CORE_EXPORT static ExternalRefCountData *getAndRef(const QObject *);
  18. Q_CORE_EXPORT void setQObjectShared(const QObject *, bool enable);
  19. Q_CORE_EXPORT void checkQObjectShared(const QObject *);
  20. #endif
  21. inline void checkQObjectShared(...) { }
  22. inline void setQObjectShared(...) { }
  23. inline void operator delete(void *ptr) { ::operator delete(ptr); }
  24. inline void operator delete(void *, void *) { }
  25. };

QWeakPointer的带参构造函数是私有的,引用计数结构体使用getAndRef方法获取,value保存ptr。getAndRef只接受QObject*作为参数,因此就限制了QWeakPointer私有构造时,传入的指针类型必须为QObject.
从下面的代码我们不难发现QObjectPrivate中保存了一个ExternalRefCountData的指针,此指针存在时,引用计数+1。不存在时,创建一个新的指针,并赋值引用计数(强引用为-1,弱引用为2,这个数字回头再说)后,返回此指针。

  1. QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::getAndRef(const QObject *obj)
  2. {
  3. Q_ASSERT(obj);
  4. QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject *>(obj));
  5. Q_ASSERT_X(!d->wasDeleted, "QWeakPointer", "Detected QWeakPointer creation in a QObject being deleted");
  6. ExternalRefCountData *that = d->sharedRefcount.load();
  7. if (that) {
  8. that->weakref.ref();
  9. return that;
  10. }
  11. // we can create the refcount data because it doesn't exist
  12. ExternalRefCountData *x = new ExternalRefCountData(Qt::Uninitialized);
  13. x->strongref.store(-1);
  14. x->weakref.store(2); // the QWeakPointer that called us plus the QObject itself
  15. if (!d->sharedRefcount.testAndSetRelease(0, x)) {
  16. // ~ExternalRefCountData has a Q_ASSERT, so we use this trick to
  17. // only execute this if Q_ASSERTs are enabled
  18. Q_ASSERT((x->weakref.store(0), true));
  19. delete x;
  20. x = d->sharedRefcount.loadAcquire();
  21. x->weakref.ref();
  22. }
  23. return x;
  24. }

这个时候我们看一下QObject析构时候的处理,强引用计数置0,弱引用计数-1,如果减为0则delete掉ExternalRefCountData

  1. QObject::~QObject()
  2. {
  3. Q_D(QObject);
  4. d->wasDeleted = true;
  5. d->blockSig = 0; // unblock signals so we always emit destroyed()
  6. QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.load();
  7. if (sharedRefcount) {
  8. if (sharedRefcount->strongref.load() > 0) {
  9. qWarning("QObject: shared QObject was deleted directly. The program is malformed and may crash.");
  10. // but continue deleting, it's too late to stop anyway
  11. }
  12. // indicate to all QWeakPointers that this QObject has now been deleted
  13. sharedRefcount->strongref.store(0);
  14. if (!sharedRefcount->weakref.deref())
  15. delete sharedRefcount;
  16. }

析构函数

弱引用计数-1,如果减为0则delete掉d ,但是不会delete掉value

  1. inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; }

接触循环引用

下面的例子,按照我们对智能指针的理解,在运行后Parent和Children指针均应被释放,但实际结果会告诉我们并没有

  1. #include <QApplication>
  2. #include <QScopedPointer>
  3. #include <QDebug>
  4. class Children;
  5. class Parent {
  6. public:
  7. Parent() {
  8. }
  9. ~Parent() {
  10. qDebug() << __FUNCTION__ ;
  11. }
  12. QSharedPointer<Children> ptr;
  13. };
  14. class Children {
  15. public:
  16. Children() {
  17. }
  18. ~Children() {
  19. qDebug() << __FUNCTION__ ;
  20. }
  21. QSharedPointer<Parent> ptr;
  22. };
  23. int main(int argc, char *argv[])
  24. {
  25. QApplication a(argc, argv);
  26. // 模拟一个作用域
  27. {
  28. QSharedPointer<Parent> p(new Parent());
  29. QSharedPointer<Children> c(new Children());
  30. if(p && c){
  31. p->ptr = c;
  32. c->ptr = p;
  33. }
  34. }
  35. // 出了作用域,此时指针内容销毁,猜想一下输出内容是什么
  36. return a.exec();
  37. }

对上面的代码稍作改动,将Parent和Children中的成员变量指针改成QWeakPointer,此时Parent和Children均被析构

  1. #include <QApplication>
  2. #include <QScopedPointer>
  3. #include <QDebug>
  4. class Children;
  5. class Parent {
  6. public:
  7. Parent() {
  8. }
  9. ~Parent() {
  10. qDebug() << __FUNCTION__ ;
  11. }
  12. // 这里将QSharedPointer改为QWeakPointer
  13. QWeakPointer<Children> ptr;
  14. };
  15. class Children {
  16. public:
  17. Children() {
  18. }
  19. ~Children() {
  20. qDebug() << __FUNCTION__ ;
  21. }
  22. // 这里将QSharedPointer改为QWeakPointer
  23. QWeakPointer<Parent> ptr;
  24. };
  25. int main(int argc, char *argv[])
  26. {
  27. QApplication a(argc, argv);
  28. // 模拟一个作用域
  29. {
  30. QSharedPointer<Parent> p(new Parent());
  31. QSharedPointer<Children> c(new Children());
  32. if(p && c){
  33. p->ptr = c;
  34. c->ptr = p;
  35. }
  36. }
  37. // 再猜测一下输出内容
  38. return a.exec();
  39. }

两个对象互相使用一个 QSharedPointer成员变量指向对方(你中有我,我中有你)。由于QSharedPointer是一个强引用的计数型指针,只有当引用数为0时,就会自动删除指针释放内存,但是如果循环引用,就会导致QSharedPointer指针的引用永远都不能为0,这时候就会导致内存无法释放。
所以QWeakPointer诞生了,它就是为了打破这种循环的。并且,在需要的时候变成QSharedPointer,在其他时候不干扰QSharedPointer的引用计数。它没有重载 * 和 -> 运算符,因此不可以直接通过 QWeakPointer 访问对象,典型的用法是通过 lock() 成员函数来获得 QSharedPointer,进而使用对象。

源码赏析

  1. template <class T>
  2. class QWeakPointer
  3. {
  4. typedef T *QWeakPointer:: *RestrictedBool;
  5. typedef QtSharedPointer::ExternalRefCountData Data;
  6. public:
  7. typedef T element_type;
  8. typedef T value_type;
  9. typedef value_type *pointer;
  10. typedef const value_type *const_pointer;
  11. typedef value_type &reference;
  12. typedef const value_type &const_reference;
  13. typedef qptrdiff difference_type;
  14. bool isNull() const Q_DECL_NOTHROW { return d == nullptr || d->strongref.load() == 0 || value == nullptr; }
  15. operator RestrictedBool() const Q_DECL_NOTHROW { return isNull() ? nullptr : &QWeakPointer::value; }
  16. bool operator !() const Q_DECL_NOTHROW { return isNull(); }
  17. T *data() const Q_DECL_NOTHROW { return d == nullptr || d->strongref.load() == 0 ? nullptr : value; }
  18. inline QWeakPointer() Q_DECL_NOTHROW : d(nullptr), value(nullptr) { }
  19. inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; }
  20. #ifndef QT_NO_QOBJECT
  21. // special constructor that is enabled only if X derives from QObject
  22. #if QT_DEPRECATED_SINCE(5, 0)
  23. template <class X>
  24. QT_DEPRECATED inline QWeakPointer(X *ptr) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr)
  25. { }
  26. #endif
  27. #endif
  28. #if QT_DEPRECATED_SINCE(5, 0)
  29. template <class X>
  30. QT_DEPRECATED inline QWeakPointer &operator=(X *ptr)
  31. { return *this = QWeakPointer(ptr); }
  32. #endif
  33. QWeakPointer(const QWeakPointer &other) Q_DECL_NOTHROW : d(other.d), value(other.value)
  34. { if (d) d->weakref.ref(); }
  35. #ifdef Q_COMPILER_RVALUE_REFS
  36. QWeakPointer(QWeakPointer &&other) Q_DECL_NOTHROW
  37. : d(other.d), value(other.value)
  38. {
  39. other.d = nullptr;
  40. other.value = nullptr;
  41. }
  42. QWeakPointer &operator=(QWeakPointer &&other) Q_DECL_NOTHROW
  43. { QWeakPointer moved(std::move(other)); swap(moved); return *this; }
  44. #endif
  45. QWeakPointer &operator=(const QWeakPointer &other) Q_DECL_NOTHROW
  46. {
  47. QWeakPointer copy(other);
  48. swap(copy);
  49. return *this;
  50. }
  51. void swap(QWeakPointer &other) Q_DECL_NOTHROW
  52. {
  53. qSwap(this->d, other.d);
  54. qSwap(this->value, other.value);
  55. }
  56. inline QWeakPointer(const QSharedPointer<T> &o) : d(o.d), value(o.data())
  57. { if (d) d->weakref.ref();}
  58. inline QWeakPointer &operator=(const QSharedPointer<T> &o)
  59. {
  60. internalSet(o.d, o.value);
  61. return *this;
  62. }
  63. template <class X>
  64. inline QWeakPointer(const QWeakPointer<X> &o) : d(nullptr), value(nullptr)
  65. { *this = o; }
  66. template <class X>
  67. inline QWeakPointer &operator=(const QWeakPointer<X> &o)
  68. {
  69. // conversion between X and T could require access to the virtual table
  70. // so force the operation to go through QSharedPointer
  71. *this = o.toStrongRef();
  72. return *this;
  73. }
  74. template <class X>
  75. bool operator==(const QWeakPointer<X> &o) const Q_DECL_NOTHROW
  76. { return d == o.d && value == static_cast<const T *>(o.value); }
  77. template <class X>
  78. bool operator!=(const QWeakPointer<X> &o) const Q_DECL_NOTHROW
  79. { return !(*this == o); }
  80. template <class X>
  81. inline QWeakPointer(const QSharedPointer<X> &o) : d(nullptr), value(nullptr)
  82. { *this = o; }
  83. template <class X>
  84. inline QWeakPointer &operator=(const QSharedPointer<X> &o)
  85. {
  86. QSHAREDPOINTER_VERIFY_AUTO_CAST(T, X); // if you get an error in this line, the cast is invalid
  87. internalSet(o.d, o.data());
  88. return *this;
  89. }
  90. template <class X>
  91. bool operator==(const QSharedPointer<X> &o) const Q_DECL_NOTHROW
  92. { return d == o.d; }
  93. template <class X>
  94. bool operator!=(const QSharedPointer<X> &o) const Q_DECL_NOTHROW
  95. { return !(*this == o); }
  96. inline void clear() { *this = QWeakPointer(); }
  97. inline QSharedPointer<T> toStrongRef() const { return QSharedPointer<T>(*this); }
  98. // std::weak_ptr compatibility:
  99. inline QSharedPointer<T> lock() const { return toStrongRef(); }
  100. #if defined(QWEAKPOINTER_ENABLE_ARROW)
  101. inline T *operator->() const { return data(); }
  102. #endif
  103. private:
  104. #if defined(Q_NO_TEMPLATE_FRIENDS)
  105. public:
  106. #else
  107. template <class X> friend class QSharedPointer;
  108. template <class X> friend class QPointer;
  109. #endif
  110. template <class X>
  111. inline QWeakPointer &assign(X *ptr)
  112. { return *this = QWeakPointer<X>(ptr, true); }
  113. #ifndef QT_NO_QOBJECT
  114. template <class X>
  115. inline QWeakPointer(X *ptr, bool) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr)
  116. { }
  117. #endif
  118. inline void internalSet(Data *o, T *actual)
  119. {
  120. if (d == o) return;
  121. if (o)
  122. o->weakref.ref();
  123. if (d && !d->weakref.deref())
  124. delete d;
  125. d = o;
  126. value = actual;
  127. }
  128. Data *d;
  129. T *value;
  130. };

附录

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

Qt—智能指针
Qt智能指针—QWeakPointer
QWeakPointer Class