Qt中的Q_D宏和d指针

参考Qt中的Q_D宏和d指针

在5.12.8版本中,QListWidget的构造中出现了

QListWidget::QListWidget(QWidget *parent)
    : QListView(*new QListWidgetPrivate(), parent)
{
    Q_D(QListWidget);
    d->setup();
}

在qglobal.h有如下定义

#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
// The body must be a statement:
#define Q_CAST_IGNORE_ALIGN(body) QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wcast-align") body QT_WARNING_POP
#define Q_DECLARE_PRIVATE(Class) 
    inline Class##Private* d_func() 
    { Q_CAST_IGNORE_ALIGN(return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr));) } 
    inline const Class##Private* d_func() const 
    { Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr));) } 
    friend class Class##Private;

#define Q_DECLARE_PRIVATE_D(Dptr, Class) 
    inline Class##Private* d_func() 
    { Q_CAST_IGNORE_ALIGN(return reinterpret_cast<Class##Private *>(qGetPtrHelper(Dptr));) } 
    inline const Class##Private* d_func() const 
    { Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const Class##Private *>(qGetPtrHelper(Dptr));) } 
    friend class Class##Private;

其中

#define Q_CAST_IGNORE_ALIGN(body) QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wcast-align") body QT_WARNING_POP

按qcompilerdetection.h如下定义

#elif defined(Q_CC_GNU) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)
#  define QT_WARNING_PUSH                       QT_DO_PRAGMA(GCC diagnostic push)
#  define QT_WARNING_POP                        QT_DO_PRAGMA(GCC diagnostic pop)
#  define QT_WARNING_DISABLE_GCC(text)          QT_DO_PRAGMA(GCC diagnostic ignored text)
#  define QT_WARNING_DISABLE_CLANG(text)
#  define QT_WARNING_DISABLE_INTEL(number)
#  define QT_WARNING_DISABLE_MSVC(number)
#  define QT_WARNING_DISABLE_DEPRECATED         QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")

另外

#define QT_DO_PRAGMA(text)                      _Pragma(#text)

GCC下转换后为

#define Q_CAST_IGNORE_ALIGN(body) _Pragma("GCC diagnostic push")  _Pragma("GCC diagnostic ignored "-Wcast-align"") body _Pragma("GCC diagnostic pop")

也即

#define Q_CAST_IGNORE_ALIGN(body) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" body #pragma GCC diagnostic pop

其中_Pragma见 ==> #pragma与_Pragma
#pragma warning(push)和#pragma warning(pop)作用见 ==> #pragma warning( pop ) 和#pragma warning( push )有什么用
#pragma GCC diagnostic push和#pragma GCC diagnostic pop作用见 ==> #pragma GCC diagnostic push | pop

    #pragma GCC diagnostic push
    #pragma GCC diagnostic pop
    Causes GCC to remember the state of the diagnostics as of each push, and restore to that point at each pop. If a pop has no matching push, the command-line options are restored.
          #pragma GCC diagnostic error "-Wuninitialized"
            foo(a);                       /* error is given for this one */
          #pragma GCC diagnostic push
          #pragma GCC diagnostic ignored "-Wuninitialized"
            foo(b);                       /* no diagnostic for this one */
          #pragma GCC diagnostic pop
            foo(c);                       /* error is given for this one */
          #pragma GCC diagnostic pop
            foo(d);                       /* depends on command-line options */

#pragma GCC diagnostic ignored "-Wcast-align"作用见gcc编译选项
-Wcast-align 一旦某个指针类型强制转换导致目标所需的地址对齐增加时,编译器就发出警告。
以上Q_CAST_IGNORE_ALIGN宏作用即把弃用指针类型强制转换导致目标所需的地址对齐增加时发出的编译警告

#define Q_CAST_IGNORE_ALIGN(body) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" body #pragma GCC diagnostic pop

回到d_func的定义

// The body must be a statement:
#define Q_CAST_IGNORE_ALIGN(body) QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wcast-align") body QT_WARNING_POP
#define Q_DECLARE_PRIVATE(Class) 
    inline Class##Private* d_func() 
    { Q_CAST_IGNORE_ALIGN(return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr));) } 
    inline const Class##Private* d_func() const 
    { Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr));) } 
    friend class Class##Private;

以QListWidget来进行替换即定义

inline QListWidgetPrivate* d_func() 
    { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" return reinterpret_cast<QListWidgetPrivate *>(qGetPtrHelper(Dptr)); #pragma GCC diagnostic pop }
inline const QListWidgetPrivate* d_func() const 
    { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" return reinterpret_cast<const QListWidgetPrivate *>(qGetPtrHelper(Dptr)); #pragma GCC diagnostic pop }
friend class QListWidgetPrivate;

核心即

inline QListWidgetPrivate* d_func()
{
    return reinterpret_cast<QListWidgetPrivate *>(qGetPtrHelper(Dptr));
}
inline const QListWidgetPrivate* d_func() const 
{ 
    return reinterpret_cast<const QListWidgetPrivate *>(qGetPtrHelper(Dptr)); 
}
friend class QListWidgetPrivate;

即将qGetPtrHelper(Dptr)重新解析为QListWidgetPrivate或者const QListWidgetPrivate包含一个非常版本和常版本的内联定义。同时将QListWidgetPrivate在调用位置将QListWidgetPrivate声明为友元类

再来看看qGetPtrHelper(Dptr)定义
template inline T *qGetPtrHelper(T *ptr) { return ptr; }
即返回模板参数类型的指针,假设T为int即
inline int *qGetPtrHelper(int ptr) { return ptr; }
即返回常指针指针类型
template inline auto qGetPtrHelper(const Ptr &ptr) -> decltype(ptr.operator->()) { return ptr.operator->(); }
inline auto qGetPtrHelper(const Dptr &ptr) -> decltype(ptr.operator->())
{
return ptr.operator->();
}
auto和decltype再C++11中使用参见
C++11新标准:decltype关键字
C++11 auto和decltype关键字
auto推断时会实际执行,decltype不会执行,只做分析
这里是通过auto将返回类型延后判断,并根据decltype(ptr.operator->())进行分析,因为ptr.operator->()的返回值是指针
也就是说如果入参为普通类型T则返回T
,如果是Dptr指针类型,则通过重载的->返回值进行推断,并返回该指针类型。
如果重载->并更改了返回值类型则可能并不是期望的类型重载operator->的说明

参考这个来看下外部传入的Dptr和继承自QObject的d_ptr成员

#define Q_DECLARE_PRIVATE(Class) 
    inline Class##Private* d_func() 
    { Q_CAST_IGNORE_ALIGN(return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr));) } 

#define Q_DECLARE_PRIVATE_D(Dptr, Class) 
    inline Class##Private* d_func() 
    { Q_CAST_IGNORE_ALIGN(return reinterpret_cast<Class##Private *>(qGetPtrHelper(Dptr));) } 

因为d_ptr定义位置不确定,通过Q_DECLARE_PRIVATE_D先了解下。
Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QGraphicsProxyWidget)

inline QGraphicsProxyWidgetPrivate* d_func() 
    { return reinterpret_cast<QGraphicsProxyWidgetPrivate *>(qGetPtrHelper(QGraphicsItem::d_ptr.data())); } 

即通过qGetPtrHelper(QGraphicsItem::d_ptr.data())将QGraphicsItem::d_ptr.data()返回值类型先qGetPtrHelper返回指针类型,再将该指针类型重解析为QGraphicsProxyWidgetPrivate *指针类型。

继续研究下QGraphicsItem::d_ptr.data()

class Q_WIDGETS_EXPORT QGraphicsItem
{   
protected:
    QScopedPointer<QGraphicsItemPrivate> d_ptr;
};

QScopedPointer即通过实例化QScopedPointer后的d_ptr返回data()即T也就是QGraphicsItemPrivate类型类型的数据
再通过qGetPtrHelper获取实际T的指针类型即QGraphicsItemPrivate*。

#define Q_D(Class) Class##Private * const d = d_func()

QGraphicsItemPrivate* const d = reinterpret_cast<QGraphicsItemPrivate *>(qGetPtrHelper(d_ptr));

原始d_ptr的定义在
d_ptr 和 q_ptr

原文地址:https://www.cnblogs.com/kuikuitage/p/12825032.html