当前位置: 首页 > wzjs >正文

上海做一个公司网站多少钱报价网站制作

上海做一个公司网站多少钱,报价网站制作,oss怎么做网站,wordpress关闭主循环文章目录 前言PIMLP与二进制兼容性D指针Q指针优化d指针继承Q_D和Q_Q 前言 在探索Qt源码的过程中会看到类的成员有一个d指针,d指针类型是一个private的类,这种设计模式称为PIMPL(pointer to implementation),本文根据Q…

文章目录

  • 前言
    • PIMLP与二进制兼容性
    • D指针
    • Q指针
    • 优化d指针继承
    • Q_D和Q_Q

前言

在探索Qt源码的过程中会看到类的成员有一个d指针,d指针类型是一个private的类,这种设计模式称为PIMPL(pointer to implementation),本文根据Qt官方文章介绍d指针与q指针,理解其中的设计哲学。

PIMLP与二进制兼容性

PIMPL(pointer to implementation)或称Opaque Pointer指的是将一个类的功能实现通过另一个实现类的指针隐藏起来,例如头文件有如下声明:

class Widget {public:Widget();...
private:WidgetImpl* p;
}

在cpp中实现WidgetImpl类

class WidgetImpl {// implementation
}

这种设计模式通常使用在库的实现当中,因为它有两个好处:

  1. 隐藏类实现。包括一些没必要暴露给用户的内部函数声明,以及需要的成员变量等实现细节。
  2. 库的二进制兼容。当改变实现类的数据成员时,不影响库的二进制兼容性,原本使用库的主程序不需要重新编译,只需要把库文件(动态库)进行替换即可。

让我们看一个二进制不兼容的例子,假设有如下实现,并且将其编译成WidgetLib1.0动态库:

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

当我们有一天希望升级Widget类的功能,需要新增一个数据成员,如下:

 class Widget{// ...private:Rect m_geometry;String m_stylesheet; // NEW in WidgetLib 1.1};class Label : public Widget{public:// ...String text() const{return m_text;}private:String m_text;};

此时编译出WidgetLib1.1库后,替换1.0库,这时运行主程序会发生崩溃。原因在于我们新增了一个数据成员,从而改变了类Widget的大小,当编译器在编译生成底层代码时,它会使用到数据成员的偏移量从而访问某个对象的某一个数据成员,下面是WidgetLib1.0和WidgetLib1.1简化后label对象的内存分布对比。

在这里插入图片描述
在WidgetLib1.0中,m_text在label对象偏移量为1的位置,而在WidgetLib1.1中,m_text偏移量为2。对于主程序而言,在编译使用1.0版本的主程序时,主程序中调用text()接口的代码会被翻译成访问label对象偏移量为1的位置的数据,而在升级到WidgetLib1.1后,由于主程序没有重新编译,只是替换了库,库中的m_text偏移量变为了2,但是主程序由于没有重新编译,因此它访问的仍然是偏移量为1的位置,但是此时访问到的实际上是m_stylesheet的变量。
这里text()的代码实现的翻译在主程序中而不是在lib中,是因为其实现是在头文件中写的,那么如果不是写在头文件中结果有变化吗?答案是没有,因为编译器依赖于对象的大小生成代码,并且要求编译时和运行时的对象大小是一致的,如果我们主程序中声明了一个在栈上的label对象,编译器在编译时(主程序+1.0库)认为对象的大小是2,而升级库到1.1后,主程序运行时的实际大小为3,那么在创建对象的时候就会把栈中的数据覆盖掉从而破坏栈。

至此我们得出一个结论,如果希望程序在库升级后能继续使用,我们就不能改变类的大小

D指针

解决方法就是让导出的类拥有一个指针,这个指针指向了所有内部的数据,当内部数据的成员增减时,由于这个指针只在库中用到,因此只会影响到库,对主程序而言,类的大小一直都是一个内部数据指针的大小,因此不会对主程序产生影响,这个指针在Qt中称为D指针。

 /* Since d_ptr is a pointer and is never referenced in header file(it would cause a compile error) WidgetPrivate doesn't have to be included,but forward-declared instead.The definition of the class can be written in widget.cpp orin a separate file, say widget_p.h */class WidgetPrivate;class Widget{// ...Rect geometry() const;// ... private:WidgetPrivate *d_ptr;};

在widget_p.h中

/* widget_p.h (_p means private) */
struct WidgetPrivate
{Rect geometry;String stylesheet;
};

widget.cpp

// With this #include, we can access WidgetPrivate.
#include "widget_p.h"Widget::Widget() : d_ptr(new WidgetPrivate)
{// Creation of private data
}Rect Widget::geometry() const
{// The d-ptr is only accessed in the library codereturn d_ptr->geometry;
}
class Label : public Widget
{// ...String text();private:// Each class maintains its own d-pointerLabelPrivate *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;
}

由于d指针只在库中使用,而每次发布库都会重新编译,因此Private类可以随意更改而不会影响主程序。

这种实现方式有如下好处:

  1. 二进制兼容性
  2. 隐藏实现细节。只需一个头文件和一个库。
  3. 头文件没有实现细节相关的api,用户可以更清晰的看到能使用的api
  4. 编译更快。因为所有实现细节都从头文件都移到了实现类的cpp文件中

Q指针

有时在Private实现类中我们希望访问原有类的指针,调用它的一些函数,因此在实现类中通常会保存一个指针指向原有的类,这个指针我们称为Q指针。
widget.h

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

widget_p.h

struct WidgetPrivate
{// Constructor that initializes the q-ptrWidgetPrivate(Widget *q) : q_ptr(q) { }Widget *q_ptr; // q-ptr points to the API classRect 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 codereturn d_ptr->geometry;
}

‎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指针继承

注意到Widget和Label类都声明了一个各自类的Private类指针,且子类构造函数在实例化父类时使用的是默认无参的构造函数,因此,当我们实例化Label时,会先调用基类的构造函数,然后new WidgetPrivate,接着调用子类的构造函数,然后new LabelPrivate,对于某些深度继承的类,这种设计将会造成多次的内存申请,且有多个相互独立的Private类对象存在,子类的d_ptr还会覆盖父类的同名数据成员d_ptr。解决方法是让Private类也具有继承关系,将子类的指针沿着Private类继承链向上传递。注意这种方式要求Private父类要在单独的头文件中声明(而不是直接写在cpp文件中),否则无法被其他Private子类继承。改进后的设计如下:
widget.h

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

widget_p.h

struct WidgetPrivate
{WidgetPrivate(Widget *q) : q_ptr(q) { } // constructor that initializes the q-ptrWidget *q_ptr; // q-ptr that points to the API classRect 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对象,只会发生一次内存申请,即new LabelPrivate。

Q_D和Q_Q

在Label类方法中,当我们访问d_ptr时,访问的是基类声明的WidgetPrivate类型的指针,在Label类的方法中为了访问LabelPrivate的成员text,需要向下转换

void Label::setText(const String &text)
{LabelPrivate *d = static_cast<LabelPrivate*>(d_ptr); // cast to our private typed->text = text;
}

Qt定义了Q_D函数帮我们做上述的转化,以简化代码,Q_Q函数类似:

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

其中d_func()是把d_ptr进行类似static_cast<LabelPrivate*>的类型转换。于是代码简化为:

// 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();
}

d_func()通过Q_DECLARE_PRIVATE定义了两个版本,一个返回Private类指针,一个返回const Private类指针,此外Q_DECLARE_PRIVATE还声明ClassPrivate是Class的友元类,如下:

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

我们可以在公共类(导出类)声明一个Q_DECLARE_PRIVATE,从而快速声明返回const和非const Private类指针的d_func(),以及声明Private类是当前类的友元类,如下:

class QLabel
{
private:Q_DECLARE_PRIVATE(QLabel)
};

friend class Class##Private的作用是让Private类可以访问公共类的public/protected/private接口。
注意,当我们需要使用const版本的Private类指针时,使用如下写法:

Q_D(const Label);  // 自动调用const版本的d_func()

通常d_func()是在类内部使用,不过某些情况下,也可以通过声明friend class的方式使得其它类可以访问当前类内部的数据,这些数据通常无法从当前类的公共api得到,例如,在QLabel类中声明ClassA是友元类,ClassA对象访问QLabel内部数据的方法如下:

// ClassA声明为QLabel的friend class, ClassA方法中可以有如下调用
label->d_func()->linkClickCount;

参考:

  1. https://wiki.qt.io/D-Pointer

文章转载自:

http://UuwgbAai.wpkqr.cn
http://ncsaG3uY.wpkqr.cn
http://8udfOAqr.wpkqr.cn
http://wVJrxPjb.wpkqr.cn
http://TZKPSqlm.wpkqr.cn
http://WKpwqvns.wpkqr.cn
http://oa6M2Z6G.wpkqr.cn
http://mDCbTpYh.wpkqr.cn
http://6cW6iiz7.wpkqr.cn
http://8COKNnL8.wpkqr.cn
http://IZz0LH5D.wpkqr.cn
http://CaQHgNRT.wpkqr.cn
http://mDBaCjc1.wpkqr.cn
http://qgX5Zyc4.wpkqr.cn
http://dqHjNR1d.wpkqr.cn
http://DIdlCqHy.wpkqr.cn
http://PIptUviZ.wpkqr.cn
http://mttDSTvw.wpkqr.cn
http://trGHeKfS.wpkqr.cn
http://cT4Q5lB5.wpkqr.cn
http://boQFAfec.wpkqr.cn
http://SfopaLWI.wpkqr.cn
http://mlrF05d6.wpkqr.cn
http://zrtQxv4P.wpkqr.cn
http://4Wmw46mX.wpkqr.cn
http://n9vTuQU1.wpkqr.cn
http://ARkuKv03.wpkqr.cn
http://GhMwGIAo.wpkqr.cn
http://g471SRs2.wpkqr.cn
http://WZm6bYz2.wpkqr.cn
http://www.dtcms.com/wzjs/670085.html

相关文章:

  • 河源市建设规划局网站做枸杞的网站
  • 江汉路做网站的公司做网站一般什么问题
  • 营销推广案例百度关键词优化企业
  • 网站用户体验优化wordpress 门户主题
  • 网站建设 十年学做西餐网站
  • 分享一个网站能用的怎样制作小视频
  • 大数据和网站建设那些网站分享pr做的视频
  • 房山 网站建设百度应用宝
  • 成都网站建设空间数据共享网站建设
  • 上海专业的网站建设公司湖南禹班建设集团有限公司网站
  • 西安网站建设方案托管怎么设计平台
  • 免费建立移动网站吗赔率网站怎么做
  • 绍兴网站建设模板网站宜春建设网站
  • 网站栏目页如何做幸运飞艇网站建设
  • 科技网站设计公司排行榜wordpress 当前分页
  • 如何维护建设网站服务器租用托管
  • html5网站开发实例工业设计公司是做什么的
  • 深圳网站自然优化小公司网站建设费用
  • 如何开发一个视频网站wordpress插件中心
  • 巩义网站建设费用免费建网站服务最好的公司
  • 淘客网站做百度推广wordpress新建页面显示数据
  • 南京网站设计WordPress首页id
  • 建网站公司营销型网站建设做暖暖网站
  • 建设网站基本思路备案空壳网站通知
  • php网站开发 vip北京网站建设服务
  • 做网站公司需要什么条件百度喜欢什么样的网站
  • 关于婚纱摄影的网站模板自媒体制作视频教程
  • 公司网站创建一般网站的建设步骤有哪些
  • 苏州网络推广建网站外贸网站推广渠道
  • 遵义市在哪里做网站网站系统名称怎么填