智能指针-QPointer
ssk-wh Lv4

作用

QPointer是一个受保护的指针 。它的行为类似于普通的 C++ 指针 T *,只是它会在被引用的对象被销毁时自动清除(与普通的 C++ 指针不同,在这种情况下会变成“悬空指针”)。

注意:T 必须是 QObject 的子类。
当您需要存储指向其他人拥有的 QObject 的指针时,受保护的指针很有用。在您仍然持有对它的引用时,实际指向的内容可能已经被销毁。此时您可以通过QPointer安全地测试指针的有效性。

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
template <class T>
class QPointer
{
Q_STATIC_ASSERT_X(!std::is_pointer<T>::value, "QPointer's template type must not be a pointer type");

template<typename U>
struct TypeSelector
{
typedef QObject Type;
};
template<typename U>
struct TypeSelector<const U>
{
typedef const QObject Type;
};
typedef typename TypeSelector<T>::Type QObjectType;
QWeakPointer<QObjectType> wp;
public:
inline QPointer() { }
inline QPointer(T *p) : wp(p, true) { }
// compiler-generated copy/move ctor/assignment operators are fine!
// compiler-generated dtor is fine!

...
};

构造时可不传参数,这将会构造处一个空的智能指针。
或构造时传入T类型的指针p,传递到私有成员指针QWeakPointer wp.需要注意的是,类型T必须是QObject类型或其子类的指针(为什么这样设计,后面会介绍)。
这里Qt使用了std::is_pointer去检查传入的参数是否是一个指针类型。

同时,这里还限定了传入的指针类型必须是QObject类型或其子类。
观察QWeakPointer的实现可以看到,传入的T类型指针作为wp的构造参数

1
2
3
4
5
6
7
8
#ifndef QT_NO_QOBJECT
template <class X>
inline QWeakPointer(X *ptr, bool) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr)
{ }
#endif



最终在QWeakPointer的成员变量d指针初始化时,将T类型的指针ptr传给了getAndRef函数,getAndRef函数的参数限定了为QObject,所以只能传QObject类型哦,否则在编译时一定会产生指针类型无法为QObject错误提示呢。

1
2
3
4
5
#ifndef QT_NO_QOBJECT
Q_CORE_EXPORT static ExternalRefCountData *getAndRef(const QObject *);
Q_CORE_EXPORT void setQObjectShared(const QObject *, bool enable);
Q_CORE_EXPORT void checkQObjectShared(const QObject *);
#endif

析构函数

1
2
3
4
#ifdef Q_QDOC
// Stop qdoc from complaining about missing function
~QPointer();
#endif

在实际编译过程中,Q_QDOC实际并没有定义,所以也就不会有析构函数。换言之,使用的是类的默认析构函数。毕竟构造函数下面也注释了// compiler-generated dtor is fine!
(为什么这样写呢,在我之前写的一篇根因分析中其实介绍过,这里只是qt在使用qdoc工具生成帮助文档时,如果不写析构函数,会在生成过程中产生警告)

方法一览

QPointer的方法都比较简单,都是为了让QPointer使用起来就像是一个真正的指针一样,有疑惑的地方可以查阅_源码赏析_一节。

原型 接口说明
QPointer() Constructs a guarded pointer that points to the same object that p points to.
QPointer(T *p) Constructs a guarded pointer with value nullptr.
~QPointer() Destroys the guarded pointer. Just like a normal pointer, destroying a guarded pointer does not destroy the object being pointed to.
void clear() Clears this QPointer object. This function was introduced in Qt 5.0. See also isNull().
T * data() const Returns the pointer to the object being guarded.
T * get() const Same as data(). This function is provided for STL compatibility. This function was introduced in Qt 6.0.
bool isNull() const Returns true if the referenced object has been destroyed or if there is no referenced object; otherwise returns false.
void swap(QPointer &other) Swaps the contents of this QPointer with the contents of other. This operation is very fast and never fails. This function was introduced in Qt 5.6.
T * operator T *() const Cast operator; implements pointer semantics. Because of this function you can pass a QPointer to a function where a T* is required.
T & operator*() const Dereference operator; implements pointer semantics. Just use this operator as you would with a normal C++ pointer.
T * operator->() const Overloaded arrow operator; implements pointer semantics. Just use this operator as you would with a normal C++ pointer.
QPointer & operator=(T *p) Assignment operator. This guarded pointer will now point to the same object that p points to.

功能实现

当QPointer持有的指针销毁时,我们可以使用QPointer::isNull()来判断指针的情况,先看下isNull的实现。wp是一个QWeakPointer指针。

1
2
inline bool isNull() const
{ return wp.isNull(); }

再来看下QWeakPointer的isNull的实现,当其内容成员d或value的指针为空或者d->strongref.load() == 0时,isNull返回true.这里的d对应的结构为ExternalRefCountData

1
2
3
4
5
6
7
8
9

template <class T>
class QWeakPointer
{
...

bool isNull() const Q_DECL_NOTHROW { return d == nullptr || d->strongref.load() == 0 || value == nullptr; }

...

在介绍QWeakPointer的时候,有说到QObject对象在析构时,其内部的强引用计数会置0,见《智能指针-QWeakPointer》,满足这里isNull为true的条件。

实际场景

1
2
3
4
5
6
7
8
QLabel *label = new QLabel;
label->setText("&Status:");

delete label;
label = nullptr;

if (label)
label->show();

如果使用QPointer可以这样使用,差别就在于delete label后,并不用将其置为nullptr.

1
2
3
4
5
6
7
QPointer<QLabel> label = new QLabel;
label->setText("&Status:");

delete label;

if (label)
label->show();

源码赏析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef QPOINTER_H
#define QPOINTER_H

#include <QtCore/qsharedpointer.h>
#include <QtCore/qtypeinfo.h>

#ifndef QT_NO_QOBJECT

QT_BEGIN_NAMESPACE

class QVariant;

template <class T>
class QPointer
{
Q_STATIC_ASSERT_X(!std::is_pointer<T>::value, "QPointer's template type must not be a pointer type");

template<typename U>
struct TypeSelector
{
typedef QObject Type;
};
template<typename U>
struct TypeSelector<const U>
{
typedef const QObject Type;
};
typedef typename TypeSelector<T>::Type QObjectType;
QWeakPointer<QObjectType> wp;
public:
inline QPointer() { }
inline QPointer(T *p) : wp(p, true) { }
// compiler-generated copy/move ctor/assignment operators are fine!
// compiler-generated dtor is fine!

#ifdef Q_QDOC
// Stop qdoc from complaining about missing function
~QPointer();
#endif

inline void swap(QPointer &other) { wp.swap(other.wp); }

inline QPointer<T> &operator=(T* p)
{ wp.assign(static_cast<QObjectType*>(p)); return *this; }

inline T* data() const
{ return static_cast<T*>( wp.data()); }
inline T* operator->() const
{ return data(); }
inline T& operator*() const
{ return *data(); }
inline operator T*() const
{ return data(); }

inline bool isNull() const
{ return wp.isNull(); }

inline void clear()
{ wp.clear(); }
};
template <class T> Q_DECLARE_TYPEINFO_BODY(QPointer<T>, Q_MOVABLE_TYPE);

template <class T>
inline bool operator==(const T *o, const QPointer<T> &p)
{ return o == p.operator->(); }

template<class T>
inline bool operator==(const QPointer<T> &p, const T *o)
{ return p.operator->() == o; }

template <class T>
inline bool operator==(T *o, const QPointer<T> &p)
{ return o == p.operator->(); }

template<class T>
inline bool operator==(const QPointer<T> &p, T *o)
{ return p.operator->() == o; }

template<class T>
inline bool operator==(const QPointer<T> &p1, const QPointer<T> &p2)
{ return p1.operator->() == p2.operator->(); }

template <class T>
inline bool operator!=(const T *o, const QPointer<T> &p)
{ return o != p.operator->(); }

template<class T>
inline bool operator!= (const QPointer<T> &p, const T *o)
{ return p.operator->() != o; }

template <class T>
inline bool operator!=(T *o, const QPointer<T> &p)
{ return o != p.operator->(); }

template<class T>
inline bool operator!= (const QPointer<T> &p, T *o)
{ return p.operator->() != o; }

template<class T>
inline bool operator!= (const QPointer<T> &p1, const QPointer<T> &p2)
{ return p1.operator->() != p2.operator->() ; }

template<typename T>
QPointer<T>
qPointerFromVariant(const QVariant &variant)
{
return QPointer<T>(qobject_cast<T*>(QtSharedPointer::weakPointerFromVariant_internal(variant).data()));
}

QT_END_NAMESPACE

#endif // QT_NO_QOBJECT

#endif // QPOINTER_H

附录

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

QPointer Class

 Comments