Qt--智能指针

智能指针

C++不像JAVA那样提供了自动垃圾回收机制,内存管理上更加灵活高效,却也带来了内存泄漏的风险。

C++程序员通常采用RAII(Resource Acquisition Is Initialization)机制来管理内存资源:在类的构造函数中申请资源,在析构函数中释放资源。我们必须保证malloc分配的内存最终能被free,new创建的对象能在正确的时机被delete,并且时刻警惕使用野指针导致的崩溃。相信所有C/C++的coder都被内存泄漏和野指针崩溃问题折磨过。

智能指针的使用可以在额外开销很少的情况下,规避内存泄漏和野指针的风险。恰当的使用智能指针可以让代码更加安全稳定,简洁可读。

智能指针的实现中有强引用计数和弱引用计数,强引用计数记录有多少个智能指针在引用它,当强引用计数为0时,会去调用销毁方法,默认一般是delete,弱引用计数和强引用计数类似,但为0时不会去调用销毁方法,只是返回空指针,防止使用野指针。

Qt中提供了QPointer、QScopedPointer、QSharedPointer、QWeakPointer等几种基本智能指针,关于他们的区别,我不想像其它文章那样直接从使用层面讲,我想从源码分析,会更加让我们理解这些智能指针,在切当的场合使用切当的智能指针。

C++中的智能指针都是使用模板类,模板类的一个特点就是声明和定义都在.h文件中,没有.cpp文件,因为模板类需要使用时给定类型才能特例化,编译到.o文件里,单独像传统类那样将类实现放到源文件中,#include头文件不会包含对应的定义代码,链接时就会提示找不到模板类的方法调用。

QWeakPointer

之所以先介绍QWeakPointer,是因为QPointer和QSharedPointer的实现都依赖于QWeakPointer,请看下面分解:

template <class T>
class QWeakPointer
{
    typedef T *QWeakPointer:: *RestrictedBool;
    typedef QtSharedPointer::ExternalRefCountData Data;

public:
    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;

    inline bool isNull() const { return d == Q_NULLPTR || d->strongref.load() == 0 || value == Q_NULLPTR; }
    inline operator RestrictedBool() const { return isNull() ? Q_NULLPTR : &QWeakPointer::value; }
    inline bool operator !() const { return isNull(); }
    inline T *data() const { return d == Q_NULLPTR || d->strongref.load() == 0 ? Q_NULLPTR : value; }

    inline QWeakPointer() Q_DECL_NOTHROW : d(Q_NULLPTR), value(Q_NULLPTR) { }
    inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; }

#ifndef QT_NO_QOBJECT
    // special constructor that is enabled only if X derives from QObject
#if QT_DEPRECATED_SINCE(5, 0)
    template <class X>
    QT_DEPRECATED inline QWeakPointer(X *ptr) : d(ptr ? Data::getAndRef(ptr) : Q_NULLPTR), value(ptr)
    { }
#endif
#endif

#if QT_DEPRECATED_SINCE(5, 0)
    template <class X>
    QT_DEPRECATED inline QWeakPointer &operator=(X *ptr)
    { return *this = QWeakPointer(ptr); }
#endif

    QWeakPointer(const QWeakPointer &other) Q_DECL_NOTHROW : d(other.d), value(other.value)
    { if (d) d->weakref.ref(); }
#ifdef Q_COMPILER_RVALUE_REFS
    QWeakPointer(QWeakPointer &&other) Q_DECL_NOTHROW
        : d(other.d), value(other.value)
    {
        other.d = Q_NULLPTR;
        other.value = Q_NULLPTR;
    }
    QWeakPointer &operator=(QWeakPointer &&other) Q_DECL_NOTHROW
    { QWeakPointer moved(std::move(other)); swap(moved); return *this; }
#endif
    QWeakPointer &operator=(const QWeakPointer &other) Q_DECL_NOTHROW
    {
        QWeakPointer copy(other);
        swap(copy);
        return *this;
    }

    void swap(QWeakPointer &other) Q_DECL_NOTHROW
    {
        qSwap(this->d, other.d);
        qSwap(this->value, other.value);
    }

    inline QWeakPointer(const QSharedPointer<T> &o) : d(o.d), value(o.data())
    { if (d) d->weakref.ref();}
    inline QWeakPointer &operator=(const QSharedPointer<T> &o)
    {
        internalSet(o.d, o.value);
        return *this;
    }

    template <class X>
    inline QWeakPointer(const QWeakPointer<X> &o) : d(Q_NULLPTR), value(Q_NULLPTR)
    { *this = o; }

    template <class X>
    inline QWeakPointer &operator=(const QWeakPointer<X> &o)
    {
        // conversion between X and T could require access to the virtual table
        // so force the operation to go through QSharedPointer
        *this = o.toStrongRef();
        return *this;
    }

    template <class X>
    inline bool operator==(const QWeakPointer<X> &o) const
    { return d == o.d && value == static_cast<const T *>(o.value); }

    template <class X>
    inline bool operator!=(const QWeakPointer<X> &o) const
    { return !(*this == o); }

    template <class X>
    inline QWeakPointer(const QSharedPointer<X> &o) : d(Q_NULLPTR), value(Q_NULLPTR)
    { *this = o; }

    template <class X>
    inline QWeakPointer &operator=(const QSharedPointer<X> &o)
    {
        QSHAREDPOINTER_VERIFY_AUTO_CAST(T, X); // if you get an error in this line, the cast is invalid
        internalSet(o.d, o.data());
        return *this;
    }

    template <class X>
    inline bool operator==(const QSharedPointer<X> &o) const
    { return d == o.d; }

    template <class X>
    inline bool operator!=(const QSharedPointer<X> &o) const
    { return !(*this == o); }

    inline void clear() { *this = QWeakPointer(); }

    inline QSharedPointer<T> toStrongRef() const { return QSharedPointer<T>(*this); }
    // std::weak_ptr compatibility:
    inline QSharedPointer<T> lock() const { return toStrongRef(); }

#if defined(QWEAKPOINTER_ENABLE_ARROW)
    inline T *operator->() const { return data(); }
#endif

private:

#if defined(Q_NO_TEMPLATE_FRIENDS)
public:
#else
    template <class X> friend class QSharedPointer;
    template <class X> friend class QPointer;
#endif

    template <class X>
    inline QWeakPointer &assign(X *ptr)
    { return *this = QWeakPointer<X>(ptr, true); }

#ifndef QT_NO_QOBJECT
    template <class X>
    inline QWeakPointer(X *ptr, bool) : d(ptr ? Data::getAndRef(ptr) : Q_NULLPTR), value(ptr)
    { }
#endif

    inline void internalSet(Data *o, T *actual)
    {
        if (d == o) return;
        if (o)
            o->weakref.ref();
        if (d && !d->weakref.deref())
            delete d;
        d = o;
        value = actual;
    }

    Data *d;
    T *value;
};

QWeakPointer数据成员除了保存的模板参数类型指针T* value外,还有一个Data *d;

typedef QtSharedPointer::ExternalRefCountData Data;

ExternalRefCountData 从名字来看,就知道是额外的引用计数数据

转到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 是一个结构体,数据成员有一个弱引用计数weakref,一个强引用计数strongref,一个销毁函数指针destroyer

在分析智能指针模板类时,构造函数、拷贝构造函数、析构函数、赋值函数、运算符重载是关键

QWeakPointer的无参构造函数,将引用计数结构体指针d和实际指针值value都置为空

inline QWeakPointer() Q_DECL_NOTHROW : d(Q_NULLPTR), value(Q_NULLPTR) { }

QWeakPointer的带参构造函数是私有的,引用计数结构体使用getAndRef方法获取,value保存ptr

Private:
    template <class X>
    inline QWeakPointer(X *ptr, bool) : d(ptr ? Data::getAndRef(ptr) : Q_NULLPTR), value(ptr)
    { }
QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::getAndRef(const QObject *obj)
{
    Q_ASSERT(obj);
    QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject *>(obj));
    Q_ASSERT_X(!d->wasDeleted, "QWeakPointer", "Detected QWeakPointer creation in a QObject being deleted");

    ExternalRefCountData *that = d->sharedRefcount.load();
    if (that) {
        that->weakref.ref();
        return that;
    }

    // we can create the refcount data because it doesn't exist
    ExternalRefCountData *x = new ExternalRefCountData(Qt::Uninitialized);
    x->strongref.store(-1);
    x->weakref.store(2);  // the QWeakPointer that called us plus the QObject itself
    if (!d->sharedRefcount.testAndSetRelease(0, x)) {
        delete x;
        x = d->sharedRefcount.loadAcquire();
        x->weakref.ref();
    }
    return x;
}

getAndRef的参数obj必须是QObject *类型的,这就是说这个方法保存的指针,只能是QObject及其派生类指针,后面我们会知道这个方法为QPointer带参构造函数服务,所以QPointer定义为只能保存QObject及其派生类指针。
QObject的私有数据类QObjectPrivate中保存了一个ExternalRefCountData 指针,如果为空就新建一个,初始化强引用计数strongref为-1,弱引用计数为2(这个方法用于QWeakPointer的构造函数中,所以新构造的QWeakPointer算一个,QObject本身算一个),如果为非空就将weakref弱引用计数+1,最终都返回这个引用计数数据结构体指针。

此外提下QObject的析构函数中有关对这个ExternalRefCountData 的操作:

QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.load();
if (sharedRefcount) {
    if (sharedRefcount->strongref.load() > 0) {
        qWarning("QObject: shared QObject was deleted directly. The program is malformed and may crash.");
        // but continue deleting, it's too late to stop anyway
    }

    // indicate to all QWeakPointers that this QObject has now been deleted
    sharedRefcount->strongref.store(0);
    if (!sharedRefcount->weakref.deref())
        delete sharedRefcount;
}

QObject的析构函数中,会将强引用计数置0,弱引用计数-1,如果减为0则delete掉ExternalRefCountData

QWeakPointer的拷贝构造函数,将弱引用计数+1

QWeakPointer(const QWeakPointer &other) Q_DECL_NOTHROW : d(other.d), value(other.value)
{ if (d) d->weakref.ref(); }

QWeakPointer的析构函数,弱引用计数-1,如果减为0则delete掉d ,并不会delete掉value

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

除了data方法可以返回实际指针外,没有提供任何指针操作相关的运算符

inline T *data() const { return d == Q_NULLPTR || d->strongref.load() == 0 ? Q_NULLPTR : value; }

但是它声明了两个友元类QPointer和QSharedPointer,也就可以猜测出QWeakPointer并不能直接使用,是为QPointer和QSharedPointer提供了弱引用计数的功能。

template <class X> friend class QSharedPointer;
template <class X> friend class QPointer;

QPointer

源码赏析

在qpointer.h文件中可以看到Qpointer<T>的定义

template <class T>
class QPointer
{
    Q_STATIC_ASSERT_X(!QtPrivate::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->() ; }

QPointer类中的数据成员之有一个QWeakPointer<QObjectType> wp;
QObjectType就是QObject,所以QPointer的模板类型只能是QObject或其派生类。

QPointer类无参构造函数,析构函数,赋值函数都是采用默认的

QPointer的有参构造函数,调用了QWeakPointer的有参构造函数

inline QPointer(T *p) : wp(p, true) { }

运算符重载了*,->,==,!=等指针常用运算符,实际是操纵wp.data,即实际保存的指针变量。

应用场景示例

不要被绕晕了,我们使用一个例子来说明下:

#include <QPointer>
bool func(){
    QLabel* label = new QLabel;

    QPointer<QLabel> p1(label); // new ExternalRefCountData, weakref = 2, strongref = -1

    if (1){
        QPointer<QLabel> p2(p1); // weakref = 3
    }

    // weakref = 2

    delete label; // weakref = 0, strongref = 0, delete ExternalRefCountData

    if (p1){ // strongref == 0, p1 == NULL
        p1->setText("test");
        return true;
    }

    if (label){
        label->setText("test"); // crash
    }

    return false;
}

QPointer只为Qt的QObject及其派生类服务,它的内部实现是通过QWeakPointer,它不会去销毁对象,使用QPointer的目的是防止野指针。
上述函数展现了QPointer在带参构造,拷贝构造,析构,指针被delete时的动作,调用的结果既不会return true也不会return false,而是在label->setText时崩溃掉,这就是野指针的危害。

QSharedPointer

源码赏析

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;

    inline T *data() const { return value; }
    inline bool isNull() const { return !data(); }
    inline operator RestrictedBool() const { return isNull() ? Q_NULLPTR : &QSharedPointer::value; }
    inline bool operator !() const { return isNull(); }
    inline T &operator*() const { return *data(); }
    inline T *operator->() const { return data(); }

    QSharedPointer() Q_DECL_NOTHROW : value(Q_NULLPTR), d(Q_NULLPTR) {}
    ~QSharedPointer() { deref(); }

    inline explicit QSharedPointer(T *ptr) : value(ptr) // noexcept
    { internalConstruct(ptr, QtSharedPointer::NormalDeleter()); }

    template <typename Deleter>
    inline QSharedPointer(T *ptr, Deleter deleter) : value(ptr) // throws
    { internalConstruct(ptr, deleter); }

    QSharedPointer(const QSharedPointer &other) Q_DECL_NOTHROW : value(other.value), d(other.d)
    { if (d) ref(); }
    QSharedPointer &operator=(const QSharedPointer &other) Q_DECL_NOTHROW
    {
        QSharedPointer copy(other);
        swap(copy);
        return *this;
    }
#ifdef Q_COMPILER_RVALUE_REFS
    QSharedPointer(QSharedPointer &&other) Q_DECL_NOTHROW
        : value(other.value), d(other.d)
    {
        other.d = Q_NULLPTR;
        other.value = Q_NULLPTR;
    }
    QSharedPointer &operator=(QSharedPointer &&other) Q_DECL_NOTHROW
    {
        QSharedPointer moved(std::move(other));
        swap(moved);
        return *this;
    }

    template <class X>
    QSharedPointer(QSharedPointer<X> &&other) Q_DECL_NOTHROW
        : value(other.value), d(other.d)
    {
        other.d = Q_NULLPTR;
        other.value = Q_NULLPTR;
    }

    template <class X>
    QSharedPointer &operator=(QSharedPointer<X> &&other) Q_DECL_NOTHROW
    {
        QSharedPointer moved(std::move(other));
        swap(moved);
        return *this;
    }

#endif

    template <class X>
    inline QSharedPointer(const QSharedPointer<X> &other) : value(other.value), d(other.d)
    { if (d) ref(); }

    template <class X>
    inline QSharedPointer &operator=(const QSharedPointer<X> &other)
    {
        QSharedPointer copy(other);
        swap(copy);
        return *this;
    }

    template <class X>
    inline QSharedPointer(const QWeakPointer<X> &other) : value(Q_NULLPTR), d(Q_NULLPTR)
    { *this = other; }

    template <class X>
    inline QSharedPointer<T> &operator=(const QWeakPointer<X> &other)
    { internalSet(other.d, other.value); return *this; }

    inline void swap(QSharedPointer &other)
    { this->internalSwap(other); }

    inline void reset() { clear(); }
    inline void reset(T *t)
    { QSharedPointer copy(t); swap(copy); }
    template <typename Deleter>
    inline void reset(T *t, Deleter deleter)
    { QSharedPointer copy(t, deleter); swap(copy); }

    template <class X>
    QSharedPointer<X> staticCast() const
    {
        return qSharedPointerCast<X, T>(*this);
    }

    template <class X>
    QSharedPointer<X> dynamicCast() const
    {
        return qSharedPointerDynamicCast<X, T>(*this);
    }

    template <class X>
    QSharedPointer<X> constCast() const
    {
        return qSharedPointerConstCast<X, T>(*this);
    }

#ifndef QT_NO_QOBJECT
    template <class X>
    QSharedPointer<X> objectCast() const
    {
        return qSharedPointerObjectCast<X, T>(*this);
    }
#endif

    inline void clear() { QSharedPointer copy; swap(copy); }

    QWeakPointer<T> toWeakRef() const;

#if defined(Q_COMPILER_RVALUE_REFS) && defined(Q_COMPILER_VARIADIC_TEMPLATES)
    template <typename... Args>
    static QSharedPointer create(Args && ...arguments)
    {
        typedef QtSharedPointer::ExternalRefCountWithContiguousData<T> Private;
# ifdef QT_SHAREDPOINTER_TRACK_POINTERS
        typename Private::DestroyerFn destroy = &Private::safetyCheckDeleter;
# else
        typename Private::DestroyerFn destroy = &Private::deleter;
# endif
        QSharedPointer result(Qt::Uninitialized);
        result.d = Private::create(&result.value, destroy);

        // now initialize the data
        new (result.data()) T(std::forward<Args>(arguments)...);
        result.d->setQObjectShared(result.value, true);
# ifdef QT_SHAREDPOINTER_TRACK_POINTERS
        internalSafetyCheckAdd(result.d, result.value);
# endif
        result.enableSharedFromThis(result.data());
        return result;
    }
#else
    static inline QSharedPointer create()
    {
        typedef QtSharedPointer::ExternalRefCountWithContiguousData<T> Private;
# ifdef QT_SHAREDPOINTER_TRACK_POINTERS
        typename Private::DestroyerFn destroy = &Private::safetyCheckDeleter;
# else
        typename Private::DestroyerFn destroy = &Private::deleter;
# endif
        QSharedPointer result(Qt::Uninitialized);
        result.d = Private::create(&result.value, destroy);

        // now initialize the data
        new (result.data()) T();
# ifdef QT_SHAREDPOINTER_TRACK_POINTERS
        internalSafetyCheckAdd(result.d, result.value);
# endif
        result.d->setQObjectShared(result.value, true);
        result.enableSharedFromThis(result.data());
        return result;
    }

    template <typename Arg>
    static inline QSharedPointer create(const Arg &arg)
    {
        typedef QtSharedPointer::ExternalRefCountWithContiguousData<T> Private;
# ifdef QT_SHAREDPOINTER_TRACK_POINTERS
        typename Private::DestroyerFn destroy = &Private::safetyCheckDeleter;
# else
        typename Private::DestroyerFn destroy = &Private::deleter;
# endif
        QSharedPointer result(Qt::Uninitialized);
        result.d = Private::create(&result.value, destroy);

        // now initialize the data
        new (result.data()) T(arg);
# ifdef QT_SHAREDPOINTER_TRACK_POINTERS
        internalSafetyCheckAdd(result.d, result.value);
# endif
        result.d->setQObjectShared(result.value, true);
        return result;
    }
#endif

private:
    explicit QSharedPointer(Qt::Initialization) {}

    void deref() Q_DECL_NOTHROW
    { deref(d); }
    static void deref(Data *d) Q_DECL_NOTHROW
    {
        if (!d) return;
        if (!d->strongref.deref()) {
            d->destroy();
        }
        if (!d->weakref.deref())
            delete d;
    }

    template <class X>
    inline void enableSharedFromThis(const QEnableSharedFromThis<X> *ptr)
    {
        ptr->initializeFromSharedPointer(constCast<typename QtPrivate::remove_cv<T>::type>());
    }

    inline void enableSharedFromThis(...) {}

    template <typename Deleter>
    inline void internalConstruct(T *ptr, Deleter deleter)
    {
        if (!ptr) {
            d = Q_NULLPTR;
            return;
        }

        typedef QtSharedPointer::ExternalRefCountWithCustomDeleter<T, Deleter> Private;
# ifdef QT_SHAREDPOINTER_TRACK_POINTERS
        typename Private::DestroyerFn actualDeleter = &Private::safetyCheckDeleter;
# else
        typename Private::DestroyerFn actualDeleter = &Private::deleter;
# endif
        d = Private::create(ptr, deleter, actualDeleter);

#ifdef QT_SHAREDPOINTER_TRACK_POINTERS
        internalSafetyCheckAdd(d, ptr);
#endif
        d->setQObjectShared(ptr, true);
        enableSharedFromThis(ptr);
    }

    void internalSwap(QSharedPointer &other) Q_DECL_NOTHROW
    {
        qSwap(d, other.d);
        qSwap(this->value, other.value);
    }

#if defined(Q_NO_TEMPLATE_FRIENDS)
public:
#else
    template <class X> friend class QSharedPointer;
    template <class X> friend class QWeakPointer;
    template <class X, class Y> friend QSharedPointer<X> QtSharedPointer::copyAndSetPointer(X * ptr, const QSharedPointer<Y> &src);
#endif
    void ref() const Q_DECL_NOTHROW { d->weakref.ref(); d->strongref.ref(); }

    inline void internalSet(Data *o, T *actual)
    {
        if (o) {
            // increase the strongref, but never up from zero
            // or less (-1 is used by QWeakPointer on untracked QObject)
            int tmp = o->strongref.load();
            while (tmp > 0) {
                // try to increment from "tmp" to "tmp + 1"
                if (o->strongref.testAndSetRelaxed(tmp, tmp + 1))
                    break;   // succeeded
                tmp = o->strongref.load();  // failed, try again
            }

            if (tmp > 0) {
                o->weakref.ref();
            } else {
                o->checkQObjectShared(actual);
                o = Q_NULLPTR;
            }
        }

        qSwap(d, o);
        qSwap(this->value, actual);
        if (!d || d->strongref.load() == 0)
            this->value = Q_NULLPTR;

        // dereference saved data
        deref(o);
    }

    Type *value;
    Data *d;
};

QSharedPointer的数据成员和QWeakPointer的一样,只不过QSharedPointer提供了public的带参构造函数,不像QWeakPointer需要友元类QPointer去调用私有的带参构造函数。

QSharedPointer带参构造函数除了可以指定指针外,还可以指定删除方法,默认的是delete。

inline explicit QSharedPointer(T *ptr) : value(ptr) // noexcept
{ internalConstruct(ptr, QtSharedPointer::NormalDeleter()); }

template <typename Deleter>
inline QSharedPointer(T *ptr, Deleter deleter) : value(ptr) // throws
{ internalConstruct(ptr, deleter); }

QSharedPointer析构函数,会调用deref,strongref和weakref都将-1,strongref如果为0会调用destroy方法,weakref为0会delete掉ExternalRefCountData

static void deref(Data *d) Q_DECL_NOTHROW
{
    if (!d) return;
    if (!d->strongref.deref()) {
        d->destroy();
    }
    if (!d->weakref.deref())
        delete d;
}

关于循环引用

class A : public QObject{

public:
    A() {qDebug("A");}
    ~A() {qDebug("~A");}

    QSharedPointer<B> pb;
};

class B : public QObject{

public:
    B() {qDebug("B");}
    ~B() {qDebug("~B");}

    QSharedPointer<A> pa;
};

void test(){
    QSharedPointer<A> a(new A);
    QSharedPointer<B> b(new B);
    a->pb = b;
    b->pa = a;
}

我做了下测试,发现QSharedPointer这种强引用计数型智能指针确实无法避免循环引用导致的内存泄漏。

QScopedPointer

源码赏析

template <typename T, typename Cleanup = QScopedPointerDeleter<T> >
class QScopedPointer
{
    typedef T *QScopedPointer:: *RestrictedBool;
public:
    explicit inline QScopedPointer(T *p = Q_NULLPTR) : d(p)
    {
    }

    inline ~QScopedPointer()
    {
        T *oldD = this->d;
        Cleanup::cleanup(oldD);
    }

    inline T &operator*() const
    {
        Q_ASSERT(d);
        return *d;
    }

    inline T *operator->() const
    {
        Q_ASSERT(d);
        return d;
    }

    inline bool operator!() const
    {
        return !d;
    }

#if defined(Q_QDOC)
    inline operator bool() const
    {
        return isNull() ? Q_NULLPTR : &QScopedPointer::d;
    }
#else
    inline operator RestrictedBool() const
    {
        return isNull() ? Q_NULLPTR : &QScopedPointer::d;
    }
#endif

    inline T *data() const
    {
        return d;
    }

    inline bool isNull() const
    {
        return !d;
    }

    inline void reset(T *other = Q_NULLPTR)
    {
        if (d == other)
            return;
        T *oldD = d;
        d = other;
        Cleanup::cleanup(oldD);
    }

    inline T *take()
    {
        T *oldD = d;
        d = Q_NULLPTR;
        return oldD;
    }

    void swap(QScopedPointer<T, Cleanup> &other) Q_DECL_NOTHROW
    {
        qSwap(d, other.d);
    }

    typedef T *pointer;

protected:
    T *d;

private:
    Q_DISABLE_COPY(QScopedPointer)
};

QScopedPointer故名思义,是用在某个作用域内的智能指针,通过带参构造函数来保存指针,析构函数销毁指针,不能拷贝,没有引用计数,相对于QSharedPointer来说是一种更轻量级别的智能指针,核心原理就是通过变量超过作用域时会自动调用析构函数来销毁指针。

此外,Qt为QScopedPointer提供了Cleanup模板,用来指定销毁指针的动作:

  • QScopedPointerDeleter:delete,这是默认的
  • QScopedPointerArrayDeleter:delete [],这是QScopedArrayPointer默认的
  • QScopedPointerPodDeleter:free
  • QScopedPointerDeleteLater:deletelater

应用场景示例

int func1(){
    char* data = (char*)malloc(1024);

    int i = 0,j = 1;
    if (i){
        free(data);
        data = NULL;
        return 1;
    }

    if (j){
        free(data);
        data = NULL;
        return 2;
    }

    free(data);
    data = NULL;
    return 3;
}

int func2(){
    char* data = (char*)malloc(1024);

    int i = 0,j = 1;
    int ret = 0;
    if (i){
        ret = 1;
        goto FINAL;
    }

    if (j){
        ret = 2;
        goto FINAL;
    }

    ret = 3;

FINAL:
    free(data);
    data = NULL;
    return ret;
}

int func3(){
    char* data = (char*)malloc(1024);

    int i = 0,j = 1;
    int ret = 0;
    do {
        if (i){
            ret = 1;
            break;
        }

        if (j){
            ret = 2;
            break;
        }

        ret = 3;
    }while (0);

    free(data);
    data = NULL;
    return ret;
}

int func4(){
    char* data = (char*)malloc(1024);

    QScopedPointer<char, QScopedPointerPodDeleter> p(data);

    int i = 0,j = 1;

    if (i){
        return 1;
    }

    if (j){
        return 2;
    }

    return 3;
}

示例中展示了四种写法,func1每次return前都要调用free,繁琐而且如果分支多了容易漏掉;func2使用goto的写法,这是C中常用的伎俩,但C++不推荐使用goto;func3使用do{…}while(0)的语法,使用break来退出分支,这种使用语法特性的代码可读性不好;func4就是使用我们的QScopedPointer了,我们制定了销毁模板类为QScopedPointerPodDeleter,即调用free销毁,在函数调用结束后,p销毁时会调用析构函数,继而调用free,不用我们操心内存泄漏问题,这就是QScopedPointer带来的便捷之处。

ithewei CSDN认证博客专家 c/c++ Qt libhv
编程之路,其路漫漫,吾将上下而求索
https://github.com/ithewei
https://hewei.blog.csdn.net
相关推荐
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页