d-pointer

简介

d-ponter由Trolltech公司(Qt)的Arnt Gulbrandsen命名,他首次将此技术引入到Qt,使Qt成为第一个具有二进制兼容性的C++gui库。这是一个增加私有数据成员而不破坏二进制兼容性的非常棒的办法。

d-pointer技术把私有成员变量都保存在一个私有类的指针中

大致结构如下

widget.h

// Forward-declare. The definition will be in widget.cpp or in a separate file, say widget_p.h
class WidgetPrivate;
class Widget {
    ...
    Rect geometry() const;
private:
    // d-pointer never referenced in header file.
    // Since WidgetPrivate is not defined in this header,
    // any access will be a compile error
    WidgetPrivate *d_ptr;
};
widget_p.h (_p means private)

struct WidgetPrivate {
    Rect geometry;
};
widget.cpp
#include "widget.h"
#include "widget_p.h"
Widget::Widget()
    : d_ptr(new WidgetPrivate) {// create private data
}
Rect Widget::geoemtry() const {
    // the d-ptr is only accessed in the library code
    return d_ptr->geometry;
}

有以下几个优点

1.二进制兼容性
变量的增减都在私有类中进行,故不改变类的大小和布局
2.减少编译时间
原因同上,类的头文件很少改变,只改变私有类,只会重新编译依赖私有类的源文件。
3.隐藏实现,头文件更简洁
优化
按照以上思想,Widget的派生类Label可以写成这样
label.h
class Label : public Widget {
   ...
   String text();
private:
   // each class maitains it's own d-pointer
   LabelPrivate *d_ptr;
};
label.cpp
// Unlike WidgetPrivate, we define LabelPrivate in the source file itself
struct LabelPrivate {
    String text;
};
Label::Label()
    : d_ptr(new LabelPrivate) {
}
String Label::text() {
    return d_ptr->text;
}
可以发现,构造一个Label会给WidgetPrivate和LabelPrivate各分配一次内存。如果继承层次更加深,那么内存分配次数将会更多。其实可以做些优化,类中加个以私有类引用为参数的构造函数,结果无论怎么继承,总共只分配一次私有类的内存。
widget.h
class Widget {
public:
    Widget();
    ...
protected:
    // only sublasses may access the below
    Widget(WidgetPrivate &d); // allow subclasses to initialize with their own concrete Private
    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;
};
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 "label.h"
#include "widget_p.h" // so we can access WidgetPrivate
class LabelPrivate : public WidgetPrivate {
    friend class Label
    String text;
};
Label::Label()
    : Widget(*new LabelPrivate) // initialize the d-pointer with our own Private {
}  
Label::Label(LabelPrivate &d)
    : Widget(d) {
}
优化后有一点要注意,就是私有类指针转化。由于子类的私有类都是继承父类私有类的,d_ptr其实是祖先类私有类的指针,所以使用子类指针的时候要cast一下
LabelPrivate *d = static_cast<LabelPrivate *>(d_ptr);
Qt的做法
Qt3里代码于以上类似,Qt4开始使用了大量的宏,如声明私有类指针的宏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()
其中
template <typename T> static inline T *qGetPtrHelper(T *ptr) { return ptr; }
就是返回指针

参考文献:
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值