下面在上一篇的基础上,咱们进入Qt的源代码,看看Qt4.x是如何实现 Private Classes 的。
正如前面咱们说的,或许你会看到不少相似 Q_D 或者 Q_Q 这类的宏。那么,咱们来试着看一下这样的代码:ide
- void MyClass::setFoo( int i )
- {
- Q_D(MyClass);
- d->m_foo = i;
- }
- int MyClass::foo() const
- {
- Q_D(const MyClass);
- return d->m_foo;
- }
按照传统 C++ 的类,若是咱们要实现这样的 getter 和 setter,咱们应该使用一个私有变量 _i,而后操做这个变量。按照上一篇说的 Private Class 的作法,咱们就要建一个 MyClassPrivateData 这样的类,而后使用指针对全部的数据操做进行委托。
再来看一个比较 Qt 的例子:函数
- class MyObject: public QObject
- {
- Q_OBJECT
- public:
- MyObject();
- virtual ~ MyObject();
- void setMemberX( int x );
- int memberX() const;
- void setMemberY( double y);
- double memberY() const;
- signals:
- void priorityChanged( MyObject::Priority priority );
- private:
- int m_memberX;
- double m_memberY;
- };
在来看一下 Qt 的实现:spa
- class MyObjectPrivate;
- class MyObject: public QObject
- {
- Q_OBJECT
- public:
- MyObject();
- virtual ~ MyObject();
- void setMemberX( int x );
- int memberX() const;
- void setMemberY( double y);
- double memberY() const;
- signals:
- void priorityChanged( MyObject::Priority priority );
- protected:
- MyObjectPrivate * const d_ptr;
- private:
- Q_DECLARE_PRIVATE(MyObject);
- };
这个例子很简单,一个使用传统方法实现,另外一个采用了 Qt4.x 的方法。Qt4.x 的方法被称为 D-Pointer,由于它会使用一个名为 d 的指针,正如上面写的那个 d_ptr。使用传统方法,咱们须要在 private 里面写上全部的私有变量,一般这会让整个文件变得很长,更为重要的是,用户并不须要这些信息。而使用 D-Pointer 的方法,咱们的接口变得很漂亮:再也没有那一串长长的私有变量了。你再也不须要将你的私有变量一块儿发布出去,它们就在你的 d 指针里面。若是你要修改数据类型这些信息,你也不须要去修改头文件,只需改变私有数据类便可。
须要注意的一点是,与单纯的 C++ 类不一样,若是你的私有类须要定义 signals 和 slots,就应该把这个定义放在头文件中,而不是像上一篇所说的放在 cpp 文件中。这是由于 qmake 只会检测 .h 文件中的 Q_OBJECT 宏
(这一点你们务必注意)。固然,你不该该把这样的 private class 放在你的类的同一个头文件中,由于这样作的话就没有意义了。常见作法是,定义一个 private 的头文件,例如使用 myclass_p.h 的命名方式(这也是 Qt 的命名方式)。而且记住,不要把 private 头文件放到你发布的 include 下面!由于这不是你发布的一部分,它们是私有的。而后,在你的 myclass 头文件中,使用设计
- class MyClassPrivate;
这种前向声明而不是直接指针
- #include "myclass_p.h"
这种方式。这也是为了不将私有的头文件发布出去,而且前向声明能够缩短编译时间。
在这个类的 private 部分,咱们使用了一个 MyClassPrivate 的 const 指针 d_ptr。若是你须要让这个类的子类也可以使用这个指针,就应该把这个 d_ptr 放在 protected 部分,正如上面的代码那样。而且,咱们还加上了 const 关键字,来确保它只能被初始化一次。
下面,咱们遇到了一个神奇的宏:Q_DECLARE_PRIVATE。这是干什么用的?那么,咱们先来看一下这个宏的展开:对象
- #define Q_DECLARE_PRIVATE(Class) \
- inline Class##Private* d_func() { return reinterpret_cast(qGetPtrHelper(d_ptr)); } \
- inline const Class##Private* d_func() const { return reinterpret_cast(qGetPtrHelper(d_ptr)); } \
- friend class Class##Private;
若是你看不大懂,那么就用咱们的 Q_DECLARE_PRIVATE(MyClass) 看看展开以后是什么吧:接口
- inline MyClassPrivate* d_func() { return reinterpret_cast(qGetPtrHelper(d_ptr)); }
- inline const MyClassPrivate* d_func() const { return reinterpret_cast(qGetPtrHelper(d_ptr)); }
- friend class MyClassPrivate;
它实际上建立了两个 inline 的 d_func() 函数,返回值分别是咱们的 d_ptr 指针和 const 指针。另外,它还把 MyClassPrivate 类声明为 MyClass 的 friend。这样的话,咱们就能够在 MyClass 这个类里面使用 Q_D(MyClass) 以及 Q_D(const MyClass)。还记得咱们最早看到的那段代码吗?如今咱们来看看这个 Q_D 却是是何方神圣!get
- // A template function for getting the instance to your private class instance.
- template static inline T *qGetPtrHelper(T *ptr) { return ptr; }
- // A macro for getting the d-pointer
- #define Q_D(Class) Class##Private * const d = d_func()
下面仍是本身展开一下这个宏,就成了it
- MyClassPrivate * const d = d_func()
简单来讲,Qt 为咱们把从 d_func() 获取 MyClassPrivate 指针的代码给封装起来了,这样咱们就能够比较面向对象的使用 getter 函数获取这个指针了。io
如今咱们已经比较清楚的知道 Qt 是如何使用 D-Pointer 实现咱们前面所说的信息隐藏的了。可是,还有一个问题:若是咱们把大部分代码集中到 MyClassPrivate 里面,极可能须要让 MyClassPrivate 的实现访问到 MyClass 的一些东西。如今咱们让主类经过 D-Pointer 访问 MyClassPrivate 的数据,可是怎么反过来让 MyClassPrivate 访问主类的数据呢?Qt 也提供了相应的解决方案,那就是 Q_Q 宏,例如:
- class MyObjectPrivate
- {
- public:
- MyObjectPrivate(MyObject * parent):
- q_ptr( parent ),
- m_priority(MyObject::Low)
- {}
- void foo()
- {
- // Demonstrate how to make MyObject to emit a signal
- Q_Q(MyObject);
- emit q->priorityChanged( m_priority );
- }
- // Members
- MyObject * const q_ptr;
- Q_DECLARE_PUBLIC(MyObject);
- MyObject::Priority m_priority;
- };
在 private 类 MyObjectPrivate 中,经过构造函数将主类 MyObject 的指针传给 q_ptr。而后咱们使用相似主类中使用的 Q_DECLARE_PRIVATE 的宏同样的另外的宏 Q_DECLARE_PUBLIC。这个宏所作的就是让你可以经过 Q_Q(Class) 宏使用主类指针。与 D-Pointer 不一样,这时候你须要使用的是 Q_Pointer。这两个是彻底相对的,这里也就再也不赘述。
如今咱们已经可以使用比较 Qt 的方式来使用 Private Classes 实现信息隐藏了。这不只仅是 Qt 的实现,固然,你也能够不用 Q_D 和 Q_Q,而是使用本身的方式,这些都可有可无。最主要的是,咱们了解了一种 C++ 类的设计思路,这是 Qt 的源代码教给咱们的。