阅读Qt的源代码的时候,我们经常看QD、QQ、QDECLAREPRIVATE、QDECLAREPUBLIC这几个宏,这几个宏是干什么用的呢?其实这几个宏就是实现D指针和Q指针的宏,D指针在Qt的源码中大量使用,根本目的在于解决二进制兼容问题。至于什么二进制兼容,可以查阅其他资料,这里只讲Qt中是怎么实现的。1。D指针及Q指针 我们先建一个Test类和私有类TestPrivate类,在Test类中添加dptr成员,在TestPrivate中添加qptr成员。头文件如代码1所示:ifndefTESTHdefineTESTHincludeQScopedPointerclassTestPclassTest{QDECLAREPRIVATE(Test)public:Test();virtualTest();voidsetValue(intv);intvalue()protected:QScopedPointerTestP};endifTESTH 代码1 源文件如代码2所示:includetest。hclassTestPrivate{QDECLAREPUBLIC(Test)public:T};Test::Test():dptr(newTestPrivate){dptrmvalue10;}Test::Test(){}voidTest::setValue(intv){QD(Test);if(dmvaluev){}}intTest::value()const{QD(constTest);} 代码2 在上面代码中我们使用了QD、QDECLAREPRIVATE、QDECLAREPUBLIC宏,这个几个宏如代码3所示(参看qglobal。h)templatetypenameTstaticinlineTqGetPtrHelper(Tptr){}templatetypenameWrapperstaticinlinetypenameWrapper::pointerqGetPtrHelper(constWrapperp){returnp。data();}defineQDECLAREPRIVATE(Class)inlineClassPrivatedfunc(){returnreinterpretcastClassPrivate(qGetPtrHelper(dptr));}inlineconstClassPrivatedfunc()const{returnreinterpretcastconstClassPrivate(qGetPtrHelper(dptr));}friendclassClassPdefineQDECLAREPUBLIC(Class)inlineClassqfunc(){returnstaticcastClass(qptr);}inlineconstClassqfunc()const{returnstaticcastconstClass(qptr);}friendclassCdefineQD(Class)ClassPrivateconstddfunc()defineQQ(Class)Classconstqqfunc() 代码3 然后我们可以看到展开之后的头文件如代码4所示:ifndefTESTHdefineTESTHincludeQScopedPointerclassTestPclassTest{inlineTestPrivatedfunc(){returnreinterpretcastTestPrivate(dptr。data());}inlineconstTestPrivatedfunc()const{returnreinterpretcastconstTestPrivate(dptr。data());}friendclassTestPpublic:Test();virtualTest();voidsetValue(intv);intvalue()protected:QScopedPointerTestP};endifTESTH 代码4 展开之后的源文件如代码5所示:includetest。hclassTestPrivate{inlineTestqfunc(){returnstaticcastTest(qptr);}inlineconstTestqfunc()const{returnstaticcastconstTest(qptr);}friendclassTpublic:T};Test::Test(){dptr。reset(newTestPrivate);dptrmvalue10;}Test::Test(){}voidTest::setValue(intv){TestPrivateconstddfunc();if(dmvaluev){}}intTest::value()const{constTestPrivateconstddfunc();} 代码5 展开之后我们就可以清晰地看到这几个宏做了什么。以QObject为例,在QObject的源代码中有如代码6所示,在构造函数中实现的dptr和qptr的初始化,跟上面的Test类似:头文件中(qobject。h)classQCOREEXPORTQObject{protected:QObject(QObjectPrivatedd,QObjectparentQNULLPTR);protected:QScopedPointerQObjectD}源文件中(qobject。cpp)QObject::QObject(QObjectparent):dptr(newQObjectPrivate){QD(QObject);} 代码6D指针和Q指针使用 假如我们的类是从Qt的类继承下来的,以QObject和上面的Test类为例,由于QObject中已经有了dptr成员了,而且Qt的类都会有一个类似QObject(QObjectPrivatedd,QObjectparentQNULLPTR)这样的受保护的构造函数,现在需要做的就是Test继承QObject、TestPrivate继承QObjectPrivate、在Test的构造函数中调用QObject(QObjectPrivatedd,QObjectparentQNULLPTR)这种构造函数。需要注意的是QObjectPrivate是在qobjectp。h头文件中,如果要包含这个头文件,我们需要在pro中添加QTcoreprivate,然后在代码中添加include。 修改之后的头文件如代码7所示:ifndefTESTHdefineTESTHincludeQObjectclassTestPclassTest:publicQObject{QOBJECTQDECLAREPRIVATE(Test)public:Test(QObjectparentnullptr);virtualTest();voidsetValue(intv);intvalue()};endifTESTH 代码7 修改之后的源文件如代码8所示,注意构造函数的变化和TestPrivate继承QObjectPrivate:includetest。hincludeprivateqobjectp。hclassTestPrivate:publicQObjectPrivate{QDECLAREPUBLIC(Test)public:};Test::Test(QObjectparent):QObject((newTestPrivate),parent){QD(Test);dmvalue10;}Test::Test(){}voidTest::setValue(intv){QD(Test);if(dmvaluev){}}intTest::value()const{QD(constTest);} 代码8 假如我们的类是类似最开始Test的那种,就像代码1和代码2那样实现即可,另外如果我们的d指针不是dptr而是像mptr这样该怎么办?其实Qt还有另外一个宏QDECLAREPRIVATED,只需要把QDECLAREPRIVATE(Test)替换成QDECLAREPRIVATED(mptr,Test)。 顺便讲一下QPRIVATESLOT这个宏,这个宏可以把私有类的成员函数注册共有类的槽函数。还是以Test为例,在头文件中添加代码9:private:QPRIVATESLOT(dfunc(),voidqtimeout()) 代码9 在源文件中TestPrivate中添加了一个mtimer和函数qtimeout,连接timer的timeout信号,修改之后代码如代码10所示,注意添加最后一行的includemoctest。cpp,如果不添加的话会导致链接错误:无法解析的外部符号。classTestPrivate:publicQObjectPrivate{QDECLAREPUBLIC(Test)public:QTvoidqtimeout();};Test::Test(QObjectparent):QObject((newTestPrivate),parent){QD(Test);dmvalue10;dmtimernewQTimer(this);connect(dmtimer,SIGNAL(timeout()),this,SLOT(qtimeout()));dmtimerstart(1000);}voidTestPrivate::qtimeout(){qDebug()}includemoctest。cpp 代码10 如果Test类可以让其他类继承,使用D指针的话,需要把TestPrivate的声明放到testp。h中去,就像qobjectp。h那样,继承类就可以使用includetestp。h包含TestPrivate类了。当然还需要为Test添加一个受保护的构造函数Test(TestPrivatedd,QObjectparentQNULLPTR)。感兴趣的小伙伴可以自己实现一下。看看有没有什么问题。 好了,关于D指针和Q指针就讲到这里了,大家有什么想知道了,可以在评论区告诉我,有空的时候帮大家整理出来。