QT之D指针

2023-10-29

什么是D指针

如果你已经看过到Qt源码,你会发现它经常使用Q_DQ_Q`宏。本文介绍了这些宏的用途。

Q_DQ_Q宏是一个设计模式的一部分被称为d-指针(也称为*不透明的指针*,其中一个库的实现细节可以从它的用户,并转移到执行被隐藏),另外也保护了库的二进制兼容性。

在设计类似Qt的库时,希望即使在将Qt库升级/替换为另一个版本之后,也可以继续运行动态链接到Qt的应用程序而无需重新编译。例如,如果您的应用程序CuteApp基于Qt 4.5,则应该能够将Qt库(在该应用程序附带的Windows上,在Linux上通常自动来自程序包管理器!)从版本4.5升级到Qt 4.6,而您的CuteApp使用Qt 4.5构建的窗口仍应能够运行。

什么破坏了二进制兼容性?

那么,库中的更改何时需要重新编译应用程序?让我们举一个简单的例子:

class Widget 
 { 
 // ... 
 private:
    Rect m_geometry; 
 }; 
 
 class Labelpublic Widget 
 { 
 public// ...
    string text()const 
    { 
        return m_text; 
    } 
 
 private:
     string m_text; 
 };

在这里,我们有一个小部件,其中包含几何作为成员变量。我们编译Widget并将其作为WidgetLib 1.0交付

对于WidgetLib 1.1,有人想出了一个聪明的主意来添加对样式表的支持。不费吹灰之力,我们只需添加新方法并添加新的数据成员

class Widget 
 { 
    // ... 
 private:
     Rect m_geometry; 
     string m_stylesheet; // WidgetLib 1.1中的新增功能
 }; 
 
 class Labelpublic Widget 
 { 
 public// ...
     string text()const 
     { 
         return m_text; 
     } 
 
 private:
     string m_text; 
 };
 

我们提供了带有上述更改的WidgetLib 1.1,只是发现在WidgetLib 1.0中编译并运行良好的CuteApp崩溃崩溃了!

为什么会崩溃?

原因是通过添加新的数据成员,我们最终更改了Widget和Label对象的大小。为什么这么重要?当您的C ++编译器生成代码时,它使用偏移量来访问对象内的数据(一般项目,在头文件有过改动,都需要应用程序重新编译,免除一些奇怪的问题出现)。

这是上述POD对象在内存中的外观的简化版本。

WidgetLib 1.0中的标签对象布局 WidgetLib 1.1中的标签对象布局
m_geometry <offset 0> m_geometry <offset 0>
m_stylesheet <offset 1>
m_text
m_text <offset 2>

在WidgetLib 1.0中,Label的文本成员位于(逻辑)偏移量1。由应用程序中的编译器为Label :: text()方法生成的代码转换为访问应用程序中Label对象的偏移量1。在WidgetLib 1.1中,Label的文本成员已移至(逻辑)偏移量2!由于尚未重新编译应用程序,因此它继续认为文本在偏移量1处,并最终访问了 样式表变量!

我可以肯定地说,有些人想知道为什么Label :: text()的偏移量计算代码最终出现在CuteApp二进制文件而不是WidgetLib二进制文件中。答案是在头文件中定义了Label :: text()的代码,编译器最终对其进行了*内联*。

那么,如果没有内联Label :: text(),情况会改变吗?说,Label :: text()已移至源文件?好吧,不。C ++编译器依赖于在编译时和运行时相同的对象大小。例如,堆栈缠绕/展开-如果在堆栈上创建了Label对象,则编译器将生成代码,以根据编译时Label的大小在堆栈上分配空间。由于WidgetLib 1.1中Label的大小在运行时是不同的,因此Label的构造函数将覆盖现有的堆栈数据并最终破坏堆栈。

切勿更改导出的C ++类的大小

总而言之,一旦发布了库,就永远不要更改导出(即用户可见)的C ++类的大小或布局(不要移动数据)。C ++编译器会生成代码,假设在编译应用程序,类中数据的大小或顺序不会更改。

那么,如何才能不改变对象的大小而又添加新的功能呢?

d指针

诀窍是通过仅存储单个指针来使库的所有公共类的大小保持恒定。该指针指向包含所有数据的私有/内部数据结构。内部结构的大小可以缩小或增长,而对应用程序没有任何副作用,因为仅在库代码中访问指针,并且从应用程序的角度来看,对象的大小从不改变-始终是对象的大小。指针。该指针称为d指针

下面的代码概述了这种模式的实质(本文中的所有代码都没有析构函数,当然您应该在真实代码中添加它们)。

widget.h

/*由于d_ptr是一个指针,并且从未在头文件中引用
 *(这会导致编译错误),因此不必包含WidgetPrivate,
 *而应进行前向声明。
 *该类的定义可以写在widget.cpp或
 *单独的文件中,例如widget_p.h */ 
 
 class WidgetPrivate ;
 
 class Widget 
 { 
     // ... 
     Rect geometry()const; 
     // ... 
 
 private:
     WidgetPrivate * d_ptr; 
 };

widget_p.h,它是小部件类的私有头文件

/ * widget_p.h(_p表示私有)* / 
struct WidgetPrivate 
{
    Rect geometry;
    String stylesheet;
};

widget.cpp

//通过此#include,我们可以访问WidgetPrivate。
#include "widget_p.h" 

Widget::Widget():d_ptr(new WidgetPrivate)
{ 
    //创建私有数据
} 

Rect Widget :: geometry()const 
{ 
    //仅在库代码
    return d_ptr->geometry;
}

接下来,有一个基于Widget的子类的示例。

Label.h

class Labelpublic Widget 
{ 
    // ...
    string text(); 

private//每个类维护自己的d指针
    LabelPrivate * d_ptr; 
};

Label.cpp

// Unlike WidgetPrivate, the author decided LabelPrivate
// to be defined in the source file itself
struct LabelPrivate
{
    String text;
};

Label::Label() : d_ptr(new LabelPrivate)
{
}

String Label::text()
{
    return d_ptr->text;
}

通过上述结构,CuteApp永远不会直接访问d指针。并且由于仅在WidgetLib中访问了d指针,并且每个版本都对WidgetLib进行了重新编译,因此Private类可以自由更改,而不会影响CuteApp。

d指针的其他好处

二进制兼容性并不是全部。d指针还有其他好处:

  • 隐藏实现细节-我们可以将WidgetLib与头文件和二进制文件一起提供。.cpp文件可以是封闭源。
  • 头文件中没有实现细节,可以用作API参考。
  • 由于实现所需的头文件已从头文件移至实现(源)文件,因此编译速度要快得多。

确实,上述好处显得微不足道。在Qt中使用d指针的真正原因是为了二进制兼容性以及Qt从封闭源开始的事实。

q指针

到目前为止,我们仅将d指针视为C样式的数据结构。实际上,它包含私有方法(辅助函数)。例如,LabelPrivate可能具有getLinkTargetFromPoint()帮助函数,该函数在单击鼠标时才能找到链接目标。在许多情况下,这些帮助程序方法需要访问公共类,即Label或其基类Widget中的某些功能。例如,一个辅助方法setTextAndUpdateWidget()可能要调用Widget :: update(),它是一个公共方法,用于计划重新绘制Widget。因此,WidgetPrivate存储一个指向公共类的指针,称为q指针。修改上面的q指针代码,我们得到:

widget.h

class WidgetPrivate;

class Widget
{
    // ...
    Rect geometry() const;
    // ...
private:
    WidgetPrivate *d_ptr;
};

widget_p.h

struct WidgetPrivate
{
    // Constructor that initializes the q-ptr
    WidgetPrivate(Widget *q) : q_ptr(q) { }
    Widget *q_ptr; // q-ptr points to the API class
    Rect geometry;
    String stylesheet;
};

widget.cpp

#include "widget_p.h"
// Create private data.
// Pass the 'this' pointer to initialize the q-ptr
Widget::Widget() : d_ptr(new WidgetPrivate(this))
{
}

Rect Widget::geometry() const
{
    // the d-ptr is only accessed in the library code
    return d_ptr->geometry;
}

接下来,另一个基于Widget的类。

label.h

class Label : public Widget
{
    // ...
    String text() const;

private:
    LabelPrivate *d_ptr;
};

label.cpp

// Unlike WidgetPrivate, the author decided LabelPrivate
// to be defined in the source file itself
struct LabelPrivate
{
    LabelPrivate(Label *q) : q_ptr(q) { }
    Label *q_ptr;
    String text;
};

Label::Label() : d_ptr(new LabelPrivate(this))
{
}

String Label::text()
{
    return d_ptr->text;
}

继承d指针进行优化

在上面的代码中,创建单个Label将导致为LabelPrivateWidgetPrivate分配内存。如果我们将这种策略用于Qt,则对于类似QListWidget的类,情况将变得更加糟糕-它在类继承层次结构中位于6级,这将导致最多6个内存分配(每一个继承都会分配一个d指针的内存分配)!

这可以通过为我们的私有类建立继承层次结构,并使实例化的类一直沿d指针传递来解决。

请注意,在继承d指针时,私有类的声明必须在单独的文件中,例如widget_p.h。不再可以在widget.cpp文件中声明它。

widget.h

class Widget
{
public:
    Widget();
    // ...
protected:
    // only subclasses may access the below
    // allow subclasses to initialize with their own concrete Private
    Widget(WidgetPrivate &d);
    WidgetPrivate *d_ptr;
};

widget_p.h

struct WidgetPrivate
{
    WidgetPrivate(Widget *q) : q_ptr(q) { } // constructor that initializes the q-ptr
    Widget *q_ptr; // q-ptr that points to the API class
    Rect geometry;
    String stylesheet;
};

widget.cpp

Widget::Widget() : d_ptr(new WidgetPrivate(this))
{
}

Widget::Widget(WidgetPrivate &d) : d_ptr(&d)
{
}

label.h

class Label : public Widget
{
public:
    Label();
    // ...
protected:
    Label(LabelPrivate &d); // allow Label subclasses to pass on their Private
    // notice how Label does not have a d_ptr! It just uses Widget's d_ptr.
};

label.cpp

#include "widget_p.h"

class LabelPrivate : public WidgetPrivate
{
public:
    String text;
};

Label::Label()
 : Widget(*new LabelPrivate) // initialize the d-pointer with our own Private
{
}

Label::Label(LabelPrivate &d) : Widget(d)
{
}

你看到这么使用的优点了吗?现在,当我们创建Label对象时,它将创建LabelPrivate(子类WidgetPrivate)。它将具体的d指针传递给Widget的受保护构造函数!现在,创建Label对象时,只有一个内存分配。Label还具有受保护的构造函数,其子类可以使用其提供自己的私有类。

Qt中的d指针

在Qt中,几乎每个公共类都使用d指针方法。唯一不使用它的情况是,当事先知道该类永远不会添加任何成员变量时。例如,对于QPointQRect之类的类,不希望添加任何新成员,因此数据成员直接存储在类本身中,而不是使用d指针。

注意,在Qt中,所有Private对象的基类都是QObjectPrivate

Q_D和Q_Q

我们在上一步中所做的优化的副作用是q-ptr和d-ptr的类型为WidgetWidgetPrivate。这意味着以下操作无效。

void Label :: setText(const String&text)
{ 
   //不起作用!因为d_ptr的类型为WidgetPrivate,即使它
   //指向LabelPrivate对象
   d_ptr-> text = text;
}

因此,当访问子类中的d指针时,我们需要static_cast为适当的类型。

void Label :: setText(const String&text)
{ 
    LabelPrivate * d = static_cast <LabelPrivate *>(d_ptr); //转换为私有类型
    d-> text = text; 
}

如您所见,到处都有static_cast并不是一件很漂亮的事情。相反,在src / corelib / global / qglobal.h中定义了两个宏,这些宏使其很直观:

global.h

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

label.cpp

// With Q_D you can use the members of LabelPrivate from Label
void Label::setText(const String &text)
{
    Q_D(Label);
    d->text = text;
}

// With Q_Q you can use the members of Label from LabelPrivate
void LabelPrivate::someHelperFunction()
{
    Q_Q(Label);
    q->selectAll();
}

Q_DECLARE_PRIVATE和Q_DECLARE_PUBLIC

Qt类在公共类中具有Q_DECLARE_PRIVATE宏。该宏显示为:

qglobal.h

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

可以通过以下方式使用此宏:

qlabel.h

class QLabel
{
private:
    Q_DECLARE_PRIVATE(QLabel)
};

这个想法是QLabel提供了一个函数d_func(),该函数允许访问其私有内部类。该方法本身是私有的(因为宏在qlabel.h中的私有部分内)。但是d_func()可以由QLabel的朋友(C ++朋友)调用。这对于Qt类访问信息非常有用,因为Qt类无法使用公共api访问某些QLabel信息。举一个奇怪的例子,QLabel可能会跟踪用户单击链接的次数。但是,没有公共API可以访问此信息。QStatistics是需要此信息的类。Qt开发人员将添加`````````QStatistics作为QLabel的朋友,然后QStatistics可以执行label-> d_func()-> linkClickCount`。

d_func还具有的优点是执行常量正确性:在MyClass的一个const成员函数,你需要一个Q_D(常量MyClass的),因此你只能调用const成员函数中MyClassPrivate。使用裸露的d_ptr,您还可以调用非const函数。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QT之D指针 的相关文章

  • 使用 PyQt 和 matplotlib 在可滚动小部件中显示多个绘图

    由于我没有得到答案this https stackoverflow com questions 12179893 creating a scrollable multiplot with pythons pylab我尝试用 PyQt 解决这
  • 使用 Qt 的网络服务

    我正在寻找使用 Qt 服务器端 实现 Web 服务的代码 如果您有任何信息 我将不胜感激 Regards 您可以使用libqxt http libqxt bitbucket org doc 0 6 qxtweb html实现服务器端Web服
  • 常量类成员、赋值运算符和 QList

    请确认我是否正确并告诉我是否有更好的解决方案 我了解具有常量成员的对象 例如int const width 无法由编译器隐式创建的合成赋值运算符处理 但是 QList 我想 std list 也是如此 需要一个有效的赋值运算符 因此 当我想
  • 扩展 TabViewStyle styleData

    我目前正在尝试找到一种更好的方法来执行此操作 将图标添加到选项卡 现在 我正在放弃 styleData title 以包含图标源 但如果能够扩展 styleData 就更好了 这样我就可以包含其他自定义属性 这是我当前的黑客 Tab tit
  • QWinTaskbarProgress 不会显示

    我使用的是windows7和Qt5 3 0 我在 MainWindow 的构造函数中添加了以下内容 但任务栏上没有显示任何内容 我错过了什么 QWinTaskbarProgress pTaskbarProgress new QWinTask
  • 如何在 Qt-Creator 中添加自定义构建步骤?

    构建我的应用程序后 我想将其复制到特定目录 在 Windows 7 上 自定义构建步骤 cmd exe c k copy MyPlugin dll HostApp Debug plugins 但我有错误 Can t run process
  • 将 C++ 代码(本机客户端)移植到浏览器(Web 应用程序)

    我有一个使用 Qt creator SDK 编写的 C 模块 我想将此代码移植到任何网页上运行 而不会对最终用户损害源代码 用户应该能够在任何浏览器 Chrome Firefox Safari Explorer 上看到此模块的输出 而无需安
  • Android 版 Qt 和 BoringSSL

    我正在开发一个基于 Qt 的 Android 应用程序 它使用 QSslSocket 下载数据 由于 Android 从 OpenSSL 转向 BoringSSL 因为依赖 OpenSSL 库的 Marshmallow Qt 程序在 And
  • QFileDialog 作为 TableView 的编辑器:如何获取结果?

    我正在使用一个QFileDialog作为某些专栏的编辑QTableView 这基本上有效 对一些焦点问题取模 请参阅here https stackoverflow com questions 22854242 qfiledialog as
  • 在 Windows 上以 QML 播放 RTSP 视频

    我正在尝试将 QML 中的 RTSP 流播放到视频标签中 如下所示 Repeater model 8 Video Layout fillWidth true Layout fillHeight true fillMode VideoOutp
  • qt 如何知道按钮被点击?

    我正在尝试编写一个程序 用声音进行一些操作 我的问题是我有 3 个播放按钮和 3 个标签 我希望无论我单击 播放 按钮 都应该播放按钮附近标签中名称的声音 我有一个没有任何参数的播放插槽 那么 如何分别连接到每个播放按钮和每个标签呢 实际上
  • 在 QtCreator 中调试时如何查看 qDebug 消息

    我正在从 Eclipse CDT 带有 Qt 集成插件 过渡到 QtCreator 2 0 但仍有一件事困扰着我 QtCreator 当我在 QtCreator 中调试时 我在Application output tab直到我停止正在调试的
  • QGraphicsSimpleTextItem“无效使用不完整类型”

    我的代码如下 指针部件 h QGraphicsSimpleTextItem text 指针控件 cpp void PointerWidget placeNumbers float spacing int currentTickNumber
  • MapItemView 在 dataChanged 信号后不会更新

    我正在使用 QMLMapItemView使用 C 的组件QAbstractListModel基于模型 这MapItemView当模型重置时 或者每当添加新项目或删除现有项目时 工作正常 但是 那MapItemView不反映对已添加项目的更改
  • PySide6.1 与 matplotlib 3.4 不兼容

    当我只安装PySide6时 GUI程序运行良好 但是一旦我安装了matplotlib及其依赖包 包括pyqt5 则GUI程序将无法运行并输出以下错误消息 This application failed to start because no
  • 仅当从 Qt 连接时网页返回 HTTP 406 错误

    我有一个测试页面设置http mlecturedownload com test qt php http mlecturedownload com test qt php有以下代码
  • 在 Qt 中创建/写入新文件

    我正在尝试写入文件 如果该文件不存在 则创建它 我在互联网上搜索过 但没有任何效果 我的代码目前看起来像这样 QString filename Data txt QFile file filename if file open QIODev
  • 如何使用样式表删除 QWizard 中的水平线?

    我正在研究一个样式表QWizard我想删除按钮上方的水平线 我已经发布了一个最小的例子here https stackoverflow com q 52538669 8570451 这个问题是由scopchanov从最小的例子中解决的 但是
  • Qt:关闭模式对话框关闭程序

    在我的 Qt 程序中 我有 2 个窗口 主窗口和子窗口 在程序中 一次仅显示这些窗口之一 主窗口有一个插槽 用于创建模式对话框 现在 假设子窗口中单击按钮的信号被发送到该插槽 在这种情况下 主窗口隐藏 子窗口可见 对话框显示得很好 但是当对
  • 更改显示的 DPI 缩放大小使 Qt 应用程序的字体大小渲染得更大

    我使用 Qt 创建了一些 GUI 应用程序 我的 GUI 应用程序包含按钮和单选按钮等控件 当我运行应用程序时 按钮内的按钮和字体看起来正常 当我将显示器的 DPI 缩放大小从 100 更改为 150 或 200 时 无论分辨率如何 控件的

随机推荐

  • DATEDIFF() 函数——返回两个日期之间的时间

    定义和用法 DATEDIFF 函数返回两个日期之间的时间 语法 DATEDIFF datepart startdate enddate startdate 和 enddate 参数是合法的日期表达式 datepart 参数可以是下列的值 实
  • HarmonyOS开发:那些开发中常见的问题汇总(一)

    前言 本来这篇文章需要讲述静态共享包如何实现远程依赖和上传以及关于静态共享包私服的搭建 非常遗憾的告诉大家 由于组织管理申请迟迟未通过 和部分文档官方权限暂未开放 关于这方面的讲解需要延后了 大概需要等到2024年第一季度 也就是来年 毕竟
  • windows Server 2012 R2安装部署

    Windows Server 2012 R2 是基于Windows8 1 以及Windows RT 8 1 界面的新一代 Windows Server 操作系统 提供企业级数据中心和混合云解决方案 易于部署 具有成本效益 以应用程序为重点
  • Delphi 通过TNetHTTPClient访问http,最新解析快手无水印视频地址链接方法

    一 解析快手无水印视频链接原理 共分三个步骤 1 通过视频分享获得视频地址短链接 如 https www kuaishou com f X7tIV0jIivYUyTk 2 通过TNetHTTPClient重定向获得视频地址长链接 如 htt
  • 把桌面从C盘改到D盘,结果直接让D盘变成了桌面,改回去发现图标变少了

    昨天晚上我一时兴起想把我电脑桌面的位置改到D盘 然后我就打开了它的属性 把位置改了 点了 应用 后弹出来一个弹窗 询问我 是否要将所有文件从原位置移动到新位置 我点了 是 其实正常来讲只要你那个新位置是个文件夹就可以 但是我当时不知道 我没
  • JavaSHA-256加解密

    Java中可以使用java security MessageDigest类来进行SHA 256加密 以下是一个使用SHA 256加密字符串的示例代码 import java security MessageDigest public cla
  • IBM发布基于OpenStack的服务

    原文地址 http www csdn net article 2013 03 05 2814349 IBM lunch service based on OpenStack 时隔13年后 IBM再一次拥抱开源 这一次 是被称为21世纪Lin
  • spi设备驱动

    include
  • iOS第三方支付集成-微信支付

    序言 说来惭愧 终于有支付的需求给我做了 哇嘎嘎 开动 文章尽量写的详细点 从自身出发 希望能给大家一点帮助 欢迎大佬指正 支付流程 步骤1 用户在商户APP中选择商品 提交订单 选择微信支付 步骤2 商户后台收到用户支付单 调用微信支付统
  • hdlm 5.9在hacmp中的配置

    hdlm 5 9是hds多路径软件最新版本的 它与以前版本有不小的改进 比如以前一个ldev 如果有4个通道 那么在os上面可以看到4个hdisk 然后这个hdisk再组成一个dlmfdrv 在5 9中只有一个hdisk 没有dlmfdrv
  • 音视频编码类型

    H264 格式介绍 avcc 前四个字节表示nalu的size 大端 Annex B 0x000001或者0x00000001开始码 nalu针对0x000000 0x000001 0x000002和0x000003插入0x03防竞争字节
  • IDEA导入Eclipse项目步骤详解

    IDEA导入Eclipse项目步骤详解 文章目录 IDEA导入Eclipse项目步骤详解 首先在idea里file gt new gt Project from Existing Sources 选中到要导入的项目 这里我选用创建新的 Cl
  • 情感分析概述

    情感分析主要研究观点挖掘 倾向性分析等 一 为什么需要观点挖掘和倾向性分析 文本信息主要包括两类 客观性事实 主观性观点 但是已有的文本分析方法主要侧重在客观性文本内容的分析和挖掘 二 什么是观点挖掘与倾向性分析 观点挖掘与倾向性分析就是从
  • Java多线程进阶(十九)—— J.U.C之synchronizer框架:CyclicBarrier

    本文首发于一世流云专栏 https segmentfault com blog 一 CyclicBarrier简介 CyclicBarrier是一个辅助同步器类 在JDK1 5时随着J U C一起引入 这个类的功能和我们之前介绍的Count
  • Jmeter录制脚本

    性能关注点 接口响应时间 50毫秒 1000毫秒 吞度量 10000万每天 tPs 每秒处理事务数 压测需求与业务操作步骤 压测对象 http news baidu com 压测页面 首页 国际频道 财经频道 步骤 访问首页 单击 国际频道
  • 测试用例的优先级

    刚接触软件测试 先熟悉一下测试用例的优先级的概念 有时会听到0级别case的说法 其实这是对具有一定优先级的测试用例的说法 在这际测试实践中 测试用例根据重要性分成一定的等级 在不通的公司 可能测试用例的等级划分有所差异 但是基本大同小异
  • 积分计算两条曲线围绕y坐标轴旋转形成的立体体积

    积分计算两条曲线围绕y坐标轴旋转形成的立体体积 和附录文章1类似 计算两条曲线y x 2和y 2x围绕y坐标轴形成的立方体体积 首先要计算积分的上限和下限 根据两者相交的点求出 0 4 外层大圆R y y 1 2 和内层小圆r y y 2的
  • 使用iptables进行入站流量过滤

    iptables是Linux内置的流量过滤工具 同时也是多种防火墙的底层实现 如fw3 在本次应用中 iptables通过丢弃不符合规则的数据包 使得未注册设备在DHCP获取ip阶段失败 无法连接到专用内网 保证系统安全 iptables使
  • 10年软件测试工程师感悟——写给还在迷茫中的朋友

    这两天和朋友谈到软件测试的发展 其实软件测试已经在不知不觉中发生了非常大的改变 前几年的软件测试行业还是一个风口 随着不断地转行人员以及毕业的大学生疯狂地涌入软件测试行业 目前软件测试行业 缺口 已经基本饱和 当然 我说的是最基础的功能测试
  • QT之D指针

    什么是D指针 如果你已经看过到Qt源码 你会发现它经常使用Q D和Q Q 宏 本文介绍了这些宏的用途 该Q D和Q Q宏是一个设计模式的一部分被称为d 指针 也称为 不透明的指针 其中一个库的实现细节可以从它的用户 并转移到执行被隐藏 另外