这个题目乍一听起来有股冷飕飕的味道, 呵呵, 别想歪了, 纯粹和封建迷信无关哦。 笔者也是无意中发现在Qt的文档里有一篇关于moc工具的limitation的文章,里头的东西让笔者还挺惊讶的, 撰文以memo。
起因是qt群里有人问一个链接的错误, 程序结构很简单, 就是designer设计主界面,在代码里用多重继承方式使用, 奇怪的错误信息如下:
moc_mainwin.cpp:39: error: ‘staticMetaObject’ is not a member of ‘Ui::MainWindow’
moc_mainwin.cpp: In member function ‘virtual void* MainWin::qt_metacast(const char*)’: moc_mainwin.cpp:56: error: ‘qt_metacast’ is not a member of ‘MainWin::qt_metacast(const char*)::QMocSuperClass’
moc_mainwin.cpp: In member function ‘virtual int MainWin::qt_metacall(QMetaObject::Call, int, void**)’: moc_mainwin.cpp:62: error: ‘qt_metacall’ is not a member of ‘MainWin::qt_metacall(QMetaObject::Call, int, void**)::QMocSuperClass’
make: *** [moc_mainwin.o] Error 1
Qt自动生成的moc文件竟然也会编译出错?这可真有点匪夷所思。 笔者把工程拿过来从头看到尾也没有看出任何错误可能会导致这个链接错误。 N长时间后,一个编译阶段报出的warning引起了笔者注意, 大意是说多重继承不能从两个QObject类继承, 这就怪了,它怎么会把我的UI类也当成是从QObject派生的呢? 有了这个提示,笔者尝试着修改多重继承的那行代码:
class MainWin: public Ui::MainWindow, public QWidget
改为
class MainWin: public QWidget, public Ui::MainWindow
结果你猜怎么着?奇迹发生了……呵呵,编译成功!原来竟然是继承的顺序造成的问题。 莫非这是Qt的bug?
给trolltech support发bug report得到了这样的回答:
This is actually a known limitation which has been documented for some time, when using multiple inheritance you have to specify the QObject based class first and then the other class. Its mentioned in the documentation at:
http://doc.trolltech.com/4.5/moc.html
under the limitations section.
原来如彼! 看来咱的道行还不够,这么重要的文档竟然从来都不知道它的存在。
仔细阅读一下居然发现还有不少Qt中和moc相关的编程限制需要我们注意, 各位看官也来受受再教育吧:
moc的功能数一数
1、处理Q_OBJECT宏和signals/slots关键字,生成信号和槽的底层代码
2、处理Q_PROPERTY()和Q_ENUM()生成property系统代码
3、处理Q_FLAGS()和Q_CLASSINFO()生成额外的类meta信息
4、不需要moc处理的代码可以用预定义的宏括起来,如下:
#ifndef Q_MOC_RUN
…
#endif
moc的限制数一数(太多了,眼花缭乱)
1、模板类不能使用信号/槽机制
2、moc不扩展宏,所以信号和槽的定义不能使用宏, 包括connect的时候也不能用宏做信号和槽的名字以及参数
3、从多个类派生时,QObject派生类必须放在第一个, 因为moc是这么认为的…(比较流氓) 这也是我们前面的例子触犯的天条
4、函数指针不能作为信号或槽的参数, 因为其格式比较复杂,moc处理不了。 但可以用typedef把它定义成简单的形式再使用。(这招可真够绝的)
5、用枚举类型或typedef的类型做信号和槽的参数时,必须fully qualified。 这个词中文不知道怎么翻译才合适,简单的说就是, 如果是在类里定义的, 必须把类的路径或者命名空间的路径都加上, 防止出现混淆。 如Qt::Alignment之类的,前面的Qt就是Alignment的qualifier, 必须加上,而且有几级加几级。
6、信号和槽不能返回引用类型
7、signals和slots关键字区域只能放置信号和槽的定义,不能放其它的如变量定义等
呵呵,这些限制条款感觉颇像不平等条约, 是不是让你大开眼界了呢? 其实这些限制有一部分应该当作bug来论处, 只是对Qt编程影响不算太大,可暂时忽略,所以被归入优先级很低的问题处理了(意思就是可能永远都不改了)。
qt文档中还有一篇关于Qt为什么没有用模板来实现信号和槽的分析文章, 也是一篇有意思的科普读物, 推荐e文好的去看看。http://doc.trolltech.com/4.5/templates.html