Pimpl技术的基本应用_XX風的博客-CSDN博客
在Object 的构造函数中:
QObject::QObject(QObject *parent)
: d_ptr(new QObjectPrivate)
{
Q_D(QObject);
d_ptr->q_ptr = this;
d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current();
d->threadData->ref();
if (parent) {
QT_TRY {
if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData))
parent = 0;
setParent(parent);
} QT_CATCH(...) {
d->threadData->deref();
QT_RETHROW;
}
}
#if QT_VERSION < 0x60000
qt_addObject(this);
#endif
if (Q_UNLIKELY(qtHookData[QHooks::AddQObject]))
reinterpret_cast(qtHookData[QHooks::AddQObject])(this);
}
可以看到这样两句:
Q_D(QObject);
d_ptr->q_ptr = this;
d->threadData =....
他们的作用是什么呢?展开宏:
qtbase\src\corelib\kernel\qobject.h 在QObject的定义中有:
class Q_CORE_EXPORT QObject
{
......
Q_DECLARE_PRIVATE(QObject)
......
protected:
QScopedPointer d_ptr;
......
};
QObjectData只是进行了简单的封装,它保存了一些简单的标志位,当前类的实例、父类和子类们等等。
class Q_CORE_EXPORT QObjectData {
public:
virtual ~QObjectData() = 0;
QObject *q_ptr;
QObject *parent;
QObjectList children;
uint isWidget : 1;
uint blockSig : 1;
uint wasDeleted : 1;
uint isDeletingChildren : 1;
uint sendChildEvents : 1;
uint receiveChildEvents : 1;
uint isWindow : 1; //for QWindow
uint deleteLaterCalled : 1;
uint unused : 24;
int postedEvents;
QDynamicMetaObjectData *metaObject;
QMetaObject *dynamicMetaObject() const;
};
展开宏Q_DECLARE_PRIVATE:qtbase\src\corelib\global\qglobal.h,可以看到它为当前类创建两个内联函数:一个是普通程序函数 d_func(),另一个是常量成员函数 d_func() const ;以及为当前类添加了友元类 friend class Class##Private;
作用:定义了当前类的内联函数d_fun()并使其指向私有实现类,同时将私有类声明为当前类的友元类
template static inline T *qGetPtrHelper(T *ptr) { return ptr; }
template static inline typename Wrapper::pointer qGetPtrHelper(const Wrapper &p) { return p.data(); }
#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;
#define Q_DECLARE_PRIVATE_D(Dptr, Class) \
inline Class##Private* d_func() { return reinterpret_cast(qGetPtrHelper(Dptr)); } \
inline const Class##Private* d_func() const { return reinterpret_cast(qGetPtrHelper(Dptr)); } \
friend class Class##Private;
#define Q_DECLARE_PUBLIC(Class) \
inline Class* q_func() { return static_cast(q_ptr); } \
inline const Class* q_func() const { return static_cast(q_ptr); } \
friend class Class;
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
在QObject 路径C:\Qt\5.9.8\msvc2015\include\QtCore\qobject.h 中定义了d_ptr指针:
protected:
QScopedPointer d_ptr;
可以看到d_ptr是一个QObjectData的智能指针,因为是protected权限的,所以QObject的所有子类都能访问它,d_func函数把它强制转换成当前类的QObjectPrivate指针类型(QObjectPrivate继承于QObjectData),并返回该指针,这样我们在每个QObject的子类中使用该宏就能完成转换。那么d_func这个函数在什么时候调用呢?接着看qobject.cpp文件的一个构造函数
QObject::QObject(QObject *parent)
: d_ptr(new QObjectPrivate)
{
//Q_D的展开形式:
//#define Q_D(QObject) QObjectPrivate * const d = d_func()
//inline QObjectPrivate* d_func() { return reinterpret_cast(qGetPtrHelper(d_ptr)); }
//template static inline typename Wrapper::pointer qGetPtrHelper(const Wrapper &p) { return p.data(); } d_ptr 是一个智能指针
Q_D(QObject);
//也就是 QObjectPrivate* const d = d_ptr.data();
d_ptr->q_ptr = this;
d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current();
d->threadData->ref();
到这里很明白了,d指针原来是通过宏Q_D获得的,也就是函数d_func的返回值,上文我们分析过了,d_func通过Q_DECLARE_PRIVATE宏来获得。
利用d_func()函数,可以避免我们每次直接拿d_ptr指针进行类型转换(因为我们有可能会在子类中使用此方法,具体我们将在后面的拓展中详述)。 在d_func()中,我们为什么不直接使用d_ptr ,而要借助qGetPtrHelper()函数呢?利用此函数,是为了适配我们使用智能指针的情况,因为此时我们要拿到真正的指针,需要调用d_ptr.data()。
Q_D的使用方式和作用:
1 仅作用于当前函数作用域,在每个函数中第一行使用
2 定义了一个QObjectPrivate的常量指针指向d_fun(),因此可以通过Q_D访问d指针
3 d指针使用智能指针,因此无需手动释放
4 在QObject中Q_DECLARE_PRIVATE 和Q_D配合使用 是为了:方便获取d指针,d指针指向class#Private(私有类)
#define Q_DECLARE_PUBLIC(Class) \
inline Class* q_func() { return static_cast(q_ptr); } \
inline const Class* q_func() const { return static_cast(q_ptr); } \
friend class Class;
#define Q_Q(Class) Class * const q = q_func()
同理,我们在私有类中,有时候需要调用主类的方法,这两个宏的作用就是为了可以在私有类中拿到主类的指针。我们在私有类的构造函数中传入主类指针,并赋值给q_ptr。因为这里是拿到主类的指针,并不存在智能指针的问题,所以此处并没有借助qGetPtrHelper()函数。
Qt采用了Pimpl机制,也就是私有实现,以QObject为基类的类的具体实现放在一个XXXPrivate中,头文件中通过宏Q_DECLARE_PRIVATE来返回将d_ptr转换为当前类的XXXPrivate的一个指针,然后cpp中通过宏Q_D来获取这个d指针。到这里你可能会有一个疑问了,QObject的子类的XXXPrivate的指针是如何传递给d_ptr的? 带着这个问题我们往下看:
首先我们先看一个QPushButton的例子,将MainWindow设为QPushButton的parent:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
connect(ui.pushButton, &QPushButton::clicked,this,&MainWindow::onButtonClicked);
QPushButton *button = new QPushButton(this);
}
我们依次查看构造函数看看发生了什么:
QPushButton,调用了QAbstractButton的一个protected权限的构造函数:
QPushButton::QPushButton(QWidget *parent)
: QAbstractButton(*new QPushButtonPrivate, parent)
{
Q_D(QPushButton);
d->init();
}
QWidget,又调用了QObject的一个proteced权限的构造函数:
/*! \internal
*/
QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WindowFlags f)
: QObject(dd, 0), QPaintDevice()
{
Q_D(QWidget);
QT_TRY {
d->init(parent, f);
} QT_CATCH(...) {
QWidgetExceptionCleaner::cleanup(this, d_func());
QT_RETHROW;
}
}
QObject 的proteced权限的构造函数,可以看到QPushButton构造函数中的*new QPushButtonPrivate,最终传递给了d_ptr。
/*!
\internal
*/
QObject::QObject(QObjectPrivate &dd, QObject *parent)
: d_ptr(&dd)
{
...
}
用一张图表示:
QWidget中并没有出现d_ptr指针,原来是从Qbject继承而来。QObject中我们新添加的那个protected构造函数传入一个QWidgetPrivate,用此给QObject中的d_ptr赋值,而这便是我们唯一的d_ptr。现在总算真正理解之前d_func()中那些类型转换的作用,就是保证我们可以拿到当前正确类型的private指针。
那么同理,ObjectPrivate是继承于QObjectData,而在QObjectData中有着QObject *q_ptr;。 所有QObject子类的私有类,均继承于ObjectPrivate,故而子类中也不会出现q_ptr,在QObject的构造函数中,我们把this指针给其赋值,在通过使用Q_Q宏,我们同样可以拿到正确类型的主类q指针。
1 隐藏接口具体实现及细节
2 提高程序编译速度
3 最大程度实现二进制兼容
3.1 二进制兼容动态库:在老版本下动态库运行新的引用程序,在不经过编译的情况下,能够在新版本的动态库下运行;在需要编译的情况下 ,不需要修改源码,我们就说动态库是兼容源代码的
3.2 要想使一个dll能够达到二进制兼容,对于类中对象/结构/数据都应保存不变,如果在类中对成员对象等进行改动则会影响对象的数据模型,导致数据在数据模型中发生变化,程序使用新版本编译后会导致程序崩溃
3.3 应对方法:为了使增加或添加项后部队数据模型的大小产生影响
(1)预先分配若干个保留空间,添加项时使用保留空间的项(使用位作用域:int n:20 即该变量预先占用几个bit的空间) ----浪费空间
(2)将预分配保留空间类型由“常规变量”-->"对象指针"
qt中以QObject基类的类的构造函数都会指定一个parent的参数,那么这个参数有什么用呢?比如上文中QPushButton,指定了this也就是MainWindow类为parent,在一次次调用父类构造函数时会调用到QWidget的一个构造函数
/*! \internal
*/
QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WindowFlags f)
: QObject(dd, 0), QPaintDevice()
{
Q_D(QWidget);
QT_TRY {
d->init(parent, f);
} QT_CATCH(...) {
QWidgetExceptionCleaner::cleanup(this, d_func());
QT_RETHROW;
}
}
参数中parent也就是new QPushButton(this) 时指定为parent的this指针。接着会调用 d->init(parent, f) 这个函数:
void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f)
{
Q_Q(QWidget);
if (Q_UNLIKELY(!qobject_cast(QCoreApplication::instance())))
qFatal("QWidget: Cannot create a QWidget without QApplication");
Q_ASSERT(allWidgets);
if (allWidgets)
allWidgets->insert(q);
...
}
又出现一个Q_Q宏,经过类似的分析发现就是定义了一个q指针指向了d_ptr指针的q_ptr成员;allWidgets时QWidgetPrivate的一个静态成员,保存了所有的以QWidget为基类的指针。这个q指向的q_ptr在最终调用QObject构造函数时初始化的:
QObject::QObject(QObjectPrivate &dd, QObject *parent)
: d_ptr(&dd)
{
Q_D(QObject);
d_ptr->q_ptr = this;
...
}
继续看 void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) 这个函数:
void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f)
{
...
else if (parentWidget)
q->setParent(parentWidget, data.window_flags);
...
}
调用的void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)这个函数:
void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
{
...
d->setParent_sys(parent, f);
...
}
调用的时void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) 这个函数:
void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f)
{
...
QObjectPrivate::setParent_helper(newparent);
...
}
调用的void QObjectPrivate::setParent_helper(QObject *o) 这个函数:
void QObjectPrivate::setParent_helper(QObject *o)
{
...
parent = o;
if (parent) {
// object hierarchies are constrained to a single thread
if (threadData != parent->d_func()->threadData) {
qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread");
parent = 0;
return;
}
parent->d_func()->children.append(q);
if(sendChildEvents && parent->d_func()->receiveChildEvents) {
if (!isWidget) {
QChildEvent e(QEvent::ChildAdded, q);
QCoreApplication::sendEvent(parent, &e);
}
}
}
...
}
QPushButton设置parent最终传递给了d->parent;我们还可以看到parent->d_func()->children.append(q);这行代码其实是获取到了parent的d指针,然后把当前的指针保存到了parent的d指针的children的变量中。至此我们大概了解了d指针中的parent和children是如何赋值的。
构造函数差不多了,来看下QObject的析构函数:
QObject::~QObject()
{
...
if (!d->children.isEmpty())
d->deleteChildren();
...
}
...
void QObjectPrivate::deleteChildren()
{
Q_ASSERT_X(!isDeletingChildren, "QObjectPrivate::deleteChildren()", "isDeletingChildren already set, did this function recurse?");
isDeletingChildren = true;
// delete children objects
// don't use qDeleteAll as the destructor of the child might
// delete siblings
for (int i = 0; i < children.count(); ++i) {
currentChildBeingDeleted = children.at(i);
children[i] = 0;
delete currentChildBeingDeleted;
}
children.clear();
currentChildBeingDeleted = 0;
isDeletingChildren = false;
}
看一下children的数据结构:
class Q_CORE_EXPORT QObjectData {
public:
virtual ~QObjectData() = 0;
QObject *q_ptr;
QObject *parent;
QObjectList children;
......
};
typedef QList QObjectList;
使用一个链表存储 孩子对象。
利用Qt 对象间的父子关系可以构成对象树
删除树中的节点时会导致对应的子树被销毁
#include
#include
void fcTest()
{
QObject* p = new QObject();
QObject* c1 = new QObject();
QObject* c2 = new QObject();
c1->setParent(p);
c2->setParent(p);
qDebug() << "c1 = " << c1;
qDebug() << "c2 = " << c2;
const QObjectList& list = p->children(); //链表对象
for(int i = 0; i < list.length(); i++)
{
qDebug() << list[i];
}
qDebug() << "p: " << p;
qDebug() << "c1 parent: " << c1->parent();
qDebug() << "p2 parent: " << c2->parent();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fcTest();
return a.exec();
}
由上面的deleteChildren可以看出
当Qt 对象被销毁时
— 将自己从父对象的 Children List 移除
— 将自己的 Children List 中的所有对象销毁
什么是Qt 对象:(定义一个类,继承于 QObject ,因此而产生的对象才叫 Qt 对象)
delete需要注意的事项:
Qt内存泄漏总结(包括检测工具)_FlyWM_的博客-CSDN博客_qt内存泄漏检测