数据隐藏之Qt中d指针详解

版权声明:本文为博主原创文章,如若转载请注明本人原始地址。 https://blog.csdn.net/Andy_93/article/details/77587797

最近看到代码有用到了Qt中的Q_D指针,就去学习了下,发现真的很好用,因此写一篇文章总结下。

//student.h
class CStudent
{
public:
	CStudent();
	~CStudent();
private:
	string m_name;
	int    m_age;
	int    m_grade;
};
如果我们这个类属于一个动态库,那么向外部提供一个dll时,就会暴露我类的数据结构,使用者便会知道,原来姓名是string类型,年龄是int类型等等,这是我不想看到的,也不符合我把其设置为private的初衷。
那怎么才能解决上面的问题呢?也许我们可以这么做:

//student.h
class CStudentDate;
class CStudent
{
public:
	CStudent();
	~CStudent();
private:
	CStudentDate* d_ptr;
};

//student.cpp
class CStudentDate
{
public:
	string m_name;
	int    m_age;
	int    m_grade;
};

      我们将CStudent的数据全部放到他的数据管理类中,将其数据管理类全部放到其cpp中实现,这样即使你像人家提供你的dll和.h文件也不会暴露你的成员的数据类型。这就是一种数据隐藏方式。其优点是隐藏了我们不想透露的数据信息,而且我们即使添加数据项如在添加一个班级成员等也不会影响CStudent类的大小。能做到二进制兼容。

       好了,我们回归到前面的d指针上来,其他d指针就是Qt为了实现信息隐藏的一种方式。其具体实现原理和上面的好了,我们回归到前面的d指针上来,其他d指针就是Qt为了实现信息隐藏的一种方式。其具体实现原理和上面的CStudent一样,相当于我们的d_ptr指针。一样,相当于我们CStudent类中的d_ptr指针。下面一起来看看Qt的 d指针是怎么实现的

以下内容来自:http://blog.csdn.net/rabinsong/article/details/9474859



如上图,这个是Qt根结点的指针的一般形式,下面来看看非根结点的一般形式,

注意这里QWidge派生自QObject,它里面没有d_ptr,但是它的成员函数可以访问d_ptr,因为 d_ptr是保护成员,且它的对象模型包含 d_ptr(这是因为派生类继承父类的所有成员)。


下面我们来看看Qt对上述两种情况是怎么实现的:

qobject.h文件:
  
QObjectData {  
public:  
    QObject *q_ptr;  
    ...  
};  
  
class Q_CORE_EXPORT QObject  
{  
    ...  
    Q_DECLARE_PRIVATE(QObject)  
public:  
    Q_INVOKABLE explicit QObject(QObject *parent=0);  
    virtual ~QObject();  
    ...  
  
protected:  
    QObject(QObjectPrivate &dd, QObject *parent = 0);  
    ...      
      
protected:  
    QScopedPointer<QObjectData> d_ptr;  
    ...  
};  
如上,在这里我算去了其他的项,只保留了于d_ptr有关的项,首先来看看Q_DECLARE_PRIVATE(QObject)是什么:
    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(QObject)翻译如下:
inline QObjectPrivate *d_func()  
{  
    return reinterpret_cast<QObjectPrivate *>(qGetPtrHelper(d_ptr));   
}  
inline const QObjectPrivate *d_func() const  
{   
    return reinterpret_cast<const QObjectPrivate *>(qGetPtrHelper(d_ptr));  
}  
friend class QObjectPrivate;

再来看看qGetPtrHelper的定义:

template <typename T> static inline T *qGetPtrHelper(T *ptr)  
{   
    return ptr;  
}  
再来看QScopePointer,它类似于智能指针,这样不用关心 d_ptr的释放,当离开QScopePointer的作用范围,QScopePointer会自动释放d_ptr指向的堆内存,那麽这个指针是什么时候生成的呢?q_ptr又是什么时候赋值的呢?让我们来看看qobject.cpp的实现:
QObject::QObject(QObject *parent)  
    : d_ptr(new QObjectPrivate)  
{  
    Q_D(QObject);  
    d_ptr->q_ptr = this;  
    ...  
}  
  
QObject::QObject(QObjectPrivate &dd, QObject *parent)  
    : d_ptr(&dd)  
{  
    Q_D(QObject);  
    d_ptr->q_ptr = this;  
    ...  
}  
我们看第一个构造函数,对于根结点的d_ptr指向new QObjectPrivate,而QObjectPrivate派生自QObjectData,那麽Q_D(QObject)宏表示什么意思呢?
#define Q_D(Class) Class##Private * const d = d_func()  
Q_D(QObject);翻译如下:
  
QObjectPrivate * const d = d_func(); 
不难看出Q_D(QObject);定义了一个QObjectPrivate的常量指针,指向d_func() 的返回值,而该返回值,正是d_ptr(见头文件 d_func()的定义),因此同过Q_D宏我们就可以访问d指针了。
对于第二个构造函数稍后介绍,下面来看看非根结点的d_ptr的实现情况:
头文件:

class Q_CORE_EXPORT QObjectPrivate : public QObjectData  
{  
    Q_DECLARE_PUBLIC(QObject)  
    ...  
};  
  
class Q_GUI_EXPORT QWidgetPrivate : public QObjectPrivate  
{  
    Q_DECLARE_PUBLIC(QWidget)  
    ...  
};  
  
class Q_GUI_EXPORT QWidget : public QObject  
{  
    ...  
    Q_DECLARE_PRIVATE(QWidget)  
    ...  
public:  
    ...  
    explicit QWidget(QWidget* parent = 0, Qt::WindowFlags f = 0);  
    ...  
};  
我们首先来看看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_DECLARE_PUBLIC(QObject)翻译如下:
  
inline QObject *q_func()  
{   
    return static_cast<QObject *>(q_ptr);  
}   
inline const QObject *q_func() const   
{   
    return static_cast<const QObject *>(q_ptr);  
}   
friend class QObject;  
Q_DECLARE_PUBLIC(QWidget)翻译如下:
  
inline QWidget *q_func()  
{   
    return static_cast<QWidget *>(q_ptr);  
}   
inline const QWidget *q_func() const   
{   
    return static_cast<const QWidget *>(q_ptr);  
}   
friend class QWidget;  
注意这里的q_ptr是在QObjectData里公有声明的,QObjectPrivate,QWidgetPrivate都派生或间接派生自QObjectData,所以可以访问q_ptr。
接下来看Q_DECLARE_PRIVATE(QWidget)的翻译:
inline QWidgetPrivate *d_func()  
{  
    return reinterpret_cast<QWidgetPrivate *>(qGetPtrHelper(d_ptr));   
}  
inline const QWidgetPrivate *d_func() const  
{   
    return reinterpret_cast<const QWidgetPrivate *>(qGetPtrHelper(d_ptr));  
}  
friend class QWidgetPrivate;
接下来看看QWidget的构造函数的实现:
QWidget::QWidget(QWidget *parent, Qt::WindowFlags f)  
    : QObject(*new QWidgetPrivate, 0)  
{  
    ...  
} 
看到QObject(*new QwidgetPrivate, 0)这里调用了QObject的第二个构造函数,将d_ptr指向new QWidgetPrivate所指向的堆内存。


猜你喜欢

转载自blog.csdn.net/Andy_93/article/details/77587797