目录
4. CustomDeleter& ExternalRefCountWithCustomDeleter 的声明&定义
智能指针是什么?
1. 智能指针是使用RAII技术(惯用手法)对裸指针进行封装、隔离、管理。
2. 把堆对象转移为栈对象。
为什么使用QT智能(smart)指针?
CPP中在使用堆内存时我们必须使用指针,使用指针给我带来便利和强大的威力同时也给我们带来了很多隐患,所以指针也是把双刃剑。
常见的隐患有:
1. 我们在使用new创建一个对象的同时,由于种种原因而忘记了delete从而导致内存泄漏影响应用运行效率
2. 我们在使用一个指针的时候不知道该指针指向的对象是否已经析构,从而导致使用一个错误的野指针。
针对上述问题Qt提供一组类模板(Smart指针类)来管理C++中的裸指针, 同时提供相关重载运算符使得使用起来与指针操作无异。 指针的生命周期也交由Smart类来管理而不用程序员自己去手动管理。使用Smart指针进而避免了上述隐患的存在。
Qt提供了哪些智能指针?
QPointer、QSharedPointer、QScopedPointer、QWeakPointer、QSharedDataPointer、QExplicitlySharedDataPointer、QGuard、QtPatternist::AutoPtr
由于Qt智能指针较多,避免篇幅较长本文只对平时使用场景较多的QSharedPointer源码进行分析也比较有代表性
QSharedPointer 官方介绍:
QSharedPointer类持有对共享指针的强引用。QSharedPointer是c++中的一个自动共享指针。它的行为与用于普通目的的普通指针完全相同,包括对constness的尊重。当指针超出作用域时,QSharedPointer将删除它所持有的指针,前提是没有其他QSharedPointer对象引用它。QSharedPointer对象可以从普通指针、另一个QSharedPointer对象或通过将QWeakPointer对象提升为强引用来创建。
本文基于Qt5.12.4版本分析
QSharedPointer类模板(smart指针类)源码分析
QSharedPointer类源码篇幅较长,避免出现阅读疲劳以下分段解读。
1. QSharedPointer类模板对于模板参数T的类型重定义
template <class T> class QSharedPointer
{
typedef T *QSharedPointer:: *RestrictedBool;
typedef QtSharedPointer::ExternalRefCountData Data;
public:
typedef T Type;
typedef T element_type;
typedef T value_type;
typedef value_type *pointer;
typedef const value_type *const_pointer;
typedef value_type &reference;
typedef const value_type &const_reference;
typedef qptrdiff difference_type;
};
QSharedPointer提供了部分公有和私有的对于模板参数T的类型重定义(说明: 这里T类型并不是指一个特定类型,而是一个通用/万能类型。 我们可以把类模板看做成一个函数 QSharedPointer为函数名, template中类型列表的为函数形参列表,typedef 为返回值。 T最终的类型确认待你使用时由编译器实例化模板时才能推导出),QSharedPointer类模板分别重定义了模板入参T的类型别名、指针、常指针、引用、常引用类型。这也是模板的惯用手法和伎俩方便对于类型的获取和使用
原因有以下几点:
a. 提供公有的对于T的类型重定义方便的类外部获取和使用T及其衍生类型。
b. 类内部实现过程中统一,规范对于T的使用。
2. QSharedPointer的成员属性
template <class T> class QSharedPointer
{
// @1
Type *value;
// @2
Data *d;
};
QSharedPointer成员属性比较简洁代码段@1声明成员属性value(需要管理的裸指针), 代码段@2声明属性d。Data类型我们从第1点中可知为QtSharedPointer::ExternalRefCountData 的类型别名。主要作用为记录当前持有value的QSharedPointer对象数量和销毁QSharedPointer封装的指针。
3. QtSharedPointer::ExternalRefCountData声明&定义
struct ExternalRefCountData
{
typedef void (*DestroyerFn)(ExternalRefCountData *);
QBasicAtomicInt weakref;
QBasicAtomicInt strongref;
DestroyerFn destroyer;
inline ExternalRefCountData(DestroyerFn d)
: destroyer(d)
{
strongref.store(1);
weakref.store(1);
}
inline ExternalRefCountData(Qt::Initialization) { }
~ExternalRefCountData() { Q_ASSERT(!weakref.load()); Q_ASSERT(strongref.load() <= 0); }
void destroy() { destroyer(this); }
#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
inline void checkQObjectShared(...) { }
inline void setQObjectShared(...) { }
inline void operator delete(void *ptr) { ::operator delete(ptr); }
inline void operator delete(void *, void *) { }
};
可以看出ExternalRefCountData结构是用于管理指针计数用的,ExternalRefCountData结构中weakref、strongref这两个成员属性命名我们观察出一个用于强引用计数一个用于弱引用计数,从QBaseAtomicInt类型可以看出这是一个原子的Int类型,所以引用计数的数量计量器是一个线程安全的类型。
strongref 是内部指针的引用计数器,它跟踪指针本身的生命周期。
weakref是外部引用计数器,它跟踪对象的生存期。 用于解决QSharedPointer循环引用问题。
weakref的使用更多体现在QWeakPointer模板类中。
destroyer为一个回调函数指针,用于ExternalRefCountData 在destroy方法中回调。 从下文中我们会发现destroyer函数指针总是指向ExternalRefCountWithCustomDeleter或externalrefcountwithcontinuousdata结构得静态方法deleter or safetyCheckDeleter。
4. CustomDeleter& ExternalRefCountWithCustomDeleter 的声明&定义
// @1
template <class T, typename Klass, typename RetVal>
inline void executeDeleter(T *t, RetVal (Klass:: *memberDeleter)())
{ (t->*memberDeleter)(); }
// @2
template <class T, typename Deleter>
inline void executeDeleter(T *t, Deleter d)
{ d(t); }
// @3
struct NormalDeleter {};
//
template <class T, typename Deleter>
struct CustomDeleter
{
Deleter deleter;
T *ptr;
CustomDeleter(T *p, Deleter d) : deleter(d), ptr(p) {}
void execute() { executeDeleter(ptr, deleter); }
};
// sizeof(CustomDeleter) = sizeof(Deleter) + sizeof(void*) + padding
// for Deleter = stateless functor: 8 (32-bit) / 16 (64-bit) due to padding
// for Deleter = function pointer: 8 (32-bit) / 16 (64-bit)
// for Deleter = PMF: 12 (32-bit) / 24 (64-bit) (GCC)
// This specialization of CustomDeleter