Qt之q指针(Q_Q)d指针(Q_D)源码剖析---源码面前了无秘密

本文通过剖析Qt源码,解释了Pimpl机制如何分离接口和实现,以及d指针(Q_D)和q指针(Q_Q)在QFocusFrame中的工作原理,展示了它们在类封装和依赖管理中的作用。
摘要由CSDN通过智能技术生成

在使用Qt的过程都会听到或遇到神秘的d指针(Q_D)、q指针(Q_Q),接下来通过剖析Qt源,弄清楚他们工作原理

一、Pimpl机制

  1. 简介
// objclass.h
class ClassImpl;
class ObjClass
{
public:
	ObjClass();
	~ObjClass();
private:
	ClassImpl*		m_dateImpl;
};

// classimpl.h
class ClassImpl
{
public:
	ClassImpl();
	~ClassImpl();
	// 具体细节实现
	void fun();
};

Pimpl的思想是把类的公有使用接口与所有关于类的私有化实现分离。由于其他类使用该类的接口需要依赖类的头文件,头文件中任何变化都会影响到其他类,即使仅仅是对private或protected进行修改。Pimpl机制可以用来减少因private(protected)修改造成重新编译的代价

二、d指针(Q_D)

以QFocusFrame为例:
在这里插入图片描述
在qt中我们常常看到如上图Q_D(QFocusFrame)的使用

在qgloba.h中可以看到宏Q_D的定义如下:

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

调用d_func()函数返回类型为Class##Private的d指针变量。即在上例中则为:调用QFocusFrame的d_func()方法,返回类型为QFocusFramePrivate,d指针变量。

在这里插入图片描述
在该类中除了类方法外,没有看到任何的成员属性,但是发现在private有一个特殊的宏Q_DECLARE_PRIVATE

在qglobal.h中可以看到宏Q_DECLARE_PRIVATE的定义如下:

#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;

此宏展开后,其实就是在类中定义了一个返回值为Class##Private*,函数名为d_func的内联函数。d_func函数调用qGetPtrHelper(d_ptr)并将返回值转为Class##Private*。

在qglobal.h中可以看到qGetPtrHelper函数定义如下:

template <typename T> inline T *qGetPtrHelper(T *ptr) { return ptr; }
template <typename Ptr> inline auto qGetPtrHelper(Ptr &ptr) -> decltype(ptr.operator->()) { return ptr.operator->(); }

通过模板定义传入ptr返回ptr的一个函数。那么在上面传入d_ptr参数。d_ptr又是在什么地方定义,什么地方赋值的呢?似乎在QFocusFrame类中我们没有发现。

在该例中,QFocusFrame有以下继承关系:
在这里插入图片描述
在QObject类protected中定义了d_ptr,类型为:QScopedPointer< QObjectData >,因此QFocusFrame便继承了基类的d_ptr成员属性。
在这里插入图片描述
同时QObject类protected声明QObject(QObjectPrivate &dd, QObject *parent)构造方法,在qobject.cpp里面通过初始化列表的方式对d_ptr进行了赋值。
在这里插入图片描述

在这里插入图片描述

在QFocusFrame构造函数中调用QWidget的protected构造函数,并传入QFocusFramePrivate的实例,QWidget构造函数调用QObject的构造函数对d_ptr赋值。

到此d指针的定义和赋值就清楚了,d指针的类型为QObjectData,而在QFocusFrame构造函数中传入参数却为:QFocusFramePrivate,这又是为何?还有QFocusFramePrivate内部又做了什么呢?
在这里插入图片描述
从该类图中可以看出,QFocusFramePrivate为QObjectData的子类,因此在QFocusFrame构造时便可传入QFocusFramePrivate对d_ptr初始化。
在这里插入图片描述
QFocusFramePrivate内部定义了QFocusFrame全部的成员属性,通过此方法将类的所有私有成员变量封装到一个新类统一管理。

三、q指针(Q_Q)

在这里插入图片描述
在QFocusFramePrivate通过Q_Q拿到q指针,通过q指针访问QFocusFrame

在qglobal.h中可以看到Q_Q的定义如下:

#define Q_Q(Class) Class * const q = q_func()

调用q_func()函数返回类型为Class的q指针变量。即在上例中则为:调用QFocusFramePrivate的q_func()方法,返回类型为QFocusFrame,q指针变量。
在这里插入图片描述
同样,在QFocusFramePrivate中存在宏Q_DECLARE_PUBLIC

在qglobal.h中可以看到Q_DECLARE_PUBLIC的定义如下:

#define Q_DECLARE_PUBLIC(Class)                                    \
    inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
    inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \
    friend class Class;

到此,没有看见q_ptr的定义和赋值的地方,那么p_ptr的定义又在何处?赋值又是什么时候呢?
在这里插入图片描述
在QObjectData中声明了p_ptr,类型为QObject
在这里插入图片描述
通过类关系可知,QFocusFramePrivate中继承了QObjectData的所有属性
在这里插入图片描述
在QObject的构造函数中我们可以看到,将q_ptr赋值为this。即QFocusFrame在构造通过传入QFocusFramePrivate的实例对象,对d指针进行了初始化,同时对QFocusFramePrivate内的q_ptr初始化为this(QFocusFrame)

四、Demo

// widget.h
#ifndef WIDGET_H
#define WIDGET_H

class ObjData;
class Obj
{
protected:
    Obj(ObjData* ptr): d_ptr(ptr) {}
    ObjData* d_ptr = nullptr;
};


class Widget : public Obj
{
public:
    Widget(int w, int h);

    void print();
};

#endif // WIDGET_H

在widgeh.h中前置声明ObjData,在Obj类中定义d_ptr指针,并且提供初始化d_ptr的构造函数。Widget继承Obj,在Widget内部便有了d_ptr指针,同时在Widget中,可以调用Obj的构造函数进行初始化。

// widget.cpp
#include "widget.h"
#include <typeinfo>
#include <iostream>

class ObjData
{
public:
    virtual ~ObjData() {}
    Obj* q_ptr = nullptr;
};

class WidgetPrivate : public ObjData
{
public:
    WidgetPrivate(int w, int h): m_w(w), m_h(h) {}
    virtual ~WidgetPrivate() {}

    int width() const
    {
        return m_w;
    }
    int height() const
    {
        return m_h;
    }
private:
    int m_w;
    int m_h;
};


Widget::Widget(int w, int h)
    : Obj(new WidgetPrivate(w, h))
{
    d_ptr->q_ptr = this;
}

void Widget::print()
{
    auto d = dynamic_cast<WidgetPrivate*>(d_ptr);
    std::cout << "W=" << d->width() << "   H=" << d->height() << std::endl;
}

widget.cpp中定义ObjData,将Widget的具体实现和Widget的接口分离。对Widget细节的实现放在WidgetPrivate中,因此对Widget实现细节的修改并不会影响widget.h的改动,当有其他工程包含widget.h时,也不会导致该工程的编译。

  • 25
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Q_DECLARE_METATYPE是Qt框架中的一个宏定义。它的作用是将自定义的类型注册到Qt的元对象系统中,使得该类型可以在Qt的信号和槽机制中使用。 当我们使用QVariant来封装一个自定义指针类型的数据时,需要使用Q_DECLARE_METATYPE(Type*)来注册该指针类型,而不仅仅是Q_DECLARE_METATYPE(Type)。 如果在编译时出现了错误提示"Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system",说明该类型没有在Qt的元对象系统中注册。此时,我们需要添加Q_DECLARE_METATYPE(type)来注册该类型。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Qt源码解析3-信号和槽机制-QMetaType(qRegisterMetaType、Q_DECLARE_METATYPE、qMetaTypeId)](https://blog.csdn.net/xinqingwuji/article/details/123557682)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [error: C2338: Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt](https://blog.csdn.net/aoxuestudy/article/details/126889407)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值