Qt的D与Q指针

库的二进制兼容

对于一个已经发布的库,如果在库的某个接口类中增加了一个成员,并重新发布该库,如果使用该库的程序直接替换该库后运行时会导致程序崩溃,解决办法就是重新编译应用程序,这是因为,新库中某些接口类及继承于它的类内存布局都已变化,但是旧的应用程序是根据之前的类内存布局,在编译期间分配好了内存,如果不重新编译程序,那么就会导致访问一个数据时,其可能已被覆盖,从而导致崩溃。
所以二进制兼容是指如果自己的程序使用了第三方模块,二进制兼容可以保证在第三方模块修改之后,自己的程序可以不用重新编译就能够兼容修改后的第三方模块。

实现方法

它其实就是pimpl设计模式的具体应用。在了解二进制兼容实现方法前,引入两个概念:主类和私有类。我把二进制库提供给第三方程序的接口类称为主类,把二进制程序内部使用的类称作私有类。只要不修改主类的成员,无论怎么修改二进制库,都可以做到二进制兼容。为直观说明二进制兼容的优势,举个例子,假如我们写了一个库,提供了如下的接口:

class Interface{
public:
    Interface(){m_greeting="hello\n";}
    void say(){
        std::cout<<m_greeting;
    }
private:
    std::string m_greeting;
}

接下来我们因为新的需求,需要对库进行修改,修改如下:

class Interface{
public:
    Interface(){m_greeting="hello\n";m_name="mike";}
    void say(){
        std::cout<<m_greeting+m_name;
    }
private:
    std::string m_greeting;
    std::string m_name;
}

这时候,由于主类成员发生了修改,我们不仅要重新编译库,还要重新编译所有基于这个库的程序。

接下来我们对上述代码进行重构,修改成二进制兼容模式,二进制兼容的实现方式类似于设计模式中的Pimpl,关键点就是在主类中定义一个指向私有类的指针,简单实现方式如下:

//导出类 interface.h
class Interface{
public:
    Interface(){m_ptr = new ClassPrivate;}
    ~Interface(){delete m_ptr;}
    void say(){
        if(m_ptr!=nullptr){m_ptr->say();}
    }
private:
    ClassPrivate* m_ptr;
}
//私有类 classprivate.h
class ClassPrivate{
public:
    ClassPrivate(){m_greeting="hello\n";}
    void say(){std::cout<<m_greeting;}
private:
    std::string m_greeting;
}

当需求发生变动,我们只需要作出如下修改:

class ClassPrivate{
public:
    ClassPrivate(){m_greeting="hello\n";m_name="mike";}
    void say(){std::cout<<m_greeting+m_name;}
private:
    std::string m_greeting;
    std::string m_name;
}

因为我们没有修改主类Interface,只是对私有类进行了修改,因此不需要重新编译基于该库的程序,缩短了编译流程。

好处

私有的结构体可以随意改变,而不需要重新编译整个工程项目
隐藏实现细节
头文件中没有任何实现细节,可以作为API使用
原本在头文件的实现部分转移到乐源文件,所以编译速度有所提高

Qt中Q和D指针实现

  • d指针是在主类中使用的,主类获取私有类或类中私有变量的指针
  • q指针是在私有数据类中使用的,来获取主类对象指针
    Q_DECLARE_PRIVATE宏和Q_D宏配合用来在主类中访问私有类对象
#define Q_DECLARE_PRIVATE(Class) \
    inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \
    inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \
    friend class Class##Private;
#define Q_D(Class) Class##Private * const d = d_func()

Q_DECLARE_PUBLIC宏和Q_Q宏配合用来在私有类中访问主类对象

Q_DECLARE_PUBLIC宏和Q_Q宏
#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;
#define Q_Q(Class) Class * const q = q_func()

如下为一个完整例子:

// myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QtCore/QObject>
class MyClassPrivate;
class MyClass: public QObject
{
  Q_OBJECT
public:
  MyClass(QObject *parent = 0);
  virtual ~MyClass();
  void dummyFunc();
private:
  MyClassPrivate * const d_ptr;
  Q_DECLARE_PRIVATE(MyClass);
};
#endif // MYCLASS_H
// myclass.cpp
#include "myclass.h"
class MyClassPrivate
{
public:
  MyClassPrivate(MyClass *parent)
    : q_ptr(parent)
  {
  }
  void foobar()
  {
    Q_Q(MyClass);
    emit q->dummySignal();
  }
private:
  MyClass * const q_ptr;
  Q_DECLARE_PUBLIC(MyClass);
};
MyClass::MyClass(QObject *parent)
  : QObject(parent)
  , d_ptr(new MyClassPrivate(this))
{
}
MyClass::~MyClass()
{
  Q_D(MyClass);
  delete d;
}
void MyClass::dummyFunc()
{
  Q_D(MyClass);
  d->foobar();
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 本文翻译自 Policies/Binary Compatibility Issues With C++ 二进制兼容...
    赵者也阅读 2,158评论 0 3
  • QT的D指针 相信不少刚开始阅读Qt源代码的朋友在看到其中的Private类和诸如Q_D、Q_Q等宏时都会思考,为...
    yangpaul98阅读 1,300评论 0 1
  • D-Pointer简介 如果你经常阅读QT的源码,你会看到大量的Q_D和Q_Q宏.这篇文章将会揭开这些宏的用处.这...
    托尼章阅读 964评论 1 2
  • Qt/QML 编码规范 一. 概述 良好的编码规范可以大幅提高程序的可读和可理解性,最终目标是实现更友好的可维护性...
    赵者也阅读 1,879评论 0 5
  • 1、项目里静态分析和基于xposed动态工具介绍一下,如果不使用xposed,怎么实现动态分析工具? 静态分析工具...
    极安御信阅读 849评论 0 0