QObject ——QT所有类的基类
QObject是QT所有类的基类,QObject是Qt Object Model的核心
CODE: /* 什么是Qt Object Model,主要包括如下的东西 1.信号和槽 2.良好的对象属性,如可查询和很好看,,,~~ 3.有力的事件和事件过滤器 4.国际化字符设计 5.定时器为GUI的事件提供毫秒级的支持 6.很优秀的对象树结构 7.当对象销毁时指针自动设置为0 8.a dynamic cast that works across library boundaries,不知如何翻译 [Copy to clipboard] 你可以通过connect()连接一个信号到槽,并通过disconnect()来解除这个连接,临时中断用blockSignals(),还可以用 connectNotify()和disconnectNotify()来监听一个连接状态 每个对象有个objectName(),他的类名可以在metaObject() (查阅QMetaObject::className())找到,还可以用inherits()函数来检测他的继承关系 当一个对象销毁时发生信号 destroyed() 更多请参阅 QMetaObject, QPointer, QObjectCleanupHandler, 和Object Trees and Object Ownership. 属性: 1.对象名 objectName : QString 设置函数 QString objectName () const void setObjectName ( const QString & name ) 请参阅 metaObject() 和QMetaObject::className(). 成员方法: 1.构造函数 QObject::QObject ( QObject * parent = 0 ) 2.虚函数,析构函数 QObject::~QObject () [virtual] 要删除一个类请用deleteLater()代替 请参阅deleteLater() 2.阻塞信号 bool QObject::blockSignals ( bool block ) 成功返回true,失败为false 请参阅signalsBlocked() 3.虚保护函数,得到子类的事件 void QObject::childEvent ( QChildEvent * event ) [virtual protected] 请参阅QChildEvent和QEvent类 4.得到子类列表 const QObjectList & QObject::children () const 他在#include<QObject>里面的定义如下: CODE: typedef QList<QObject*> QObjectList; [Copy to clipboard] first()和last()分别得到子类的第1个和最后一个,同时可以用 QWidget类的 raise()或者lower()来改变,这2个都是槽,注意 请参阅findChild(), findChildren(), parent(), 和setParent() 5.静态连接信号和槽的函数 bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoCompatConnection ) [static] 使用时必须用SIGNAL() 和SLOT()宏指出信号和槽 如: CODE: QLabel *label = new QLabel; QScrollBar *scrollBar = new QScrollBar; QObject::connect(scroll, SIGNAL(valueChanged(int)), label, SLOT(setNum(int))); [Copy to clipboard] 注意: a.这里要千万注意,里面的参数不能有变量名,只能有类型,不然不能工作 错误例子: CODE: // WRONG QObject::connect(scroll, SIGNAL(valueChanged(int value)), label, SLOT(setNum(int value))); [Copy to clipboard] b.同样信号可以连接到信号,如: CODE: class MyWidget : public QWidget { Q_OBJECT public: MyWidget(); signals: void buttonClicked(); private: QPushButton *myButton; }; MyWidget::MyWidget() { myButton = new QPushButton(this); connect(myButton, SIGNAL(clicked()), this, SIGNAL(buttonClicked())); } [Copy to clipboard] 这个例子的意思是,就是MyWidget的构造函数将自己私有成员的信号以另外一种方式传出去,使其有效 c.一个信号可以连到很多信号和槽上,但是一个槽只能连到一个信号上 d.成功返回true,失败false 请参阅disconnect() 6.connect函数2 bool QObject::connect ( const QObject * sender, const char * signal, const char * method, Qt::ConnectionType type = Qt::AutoCompatConnection ) const 7.这个函数检测当有solt连接到这个信号时被调用 void QObject::connectNotify ( const char * signal ) [virtual protected] 请参阅connect() 和disconnectNotify() 8.定义自己的类的行为 void QObject::customEvent ( QEvent * event ) [virtual protected] 请参阅event() 和QEvent类 9.槽,删除此对象 void QObject::deleteLater () [slot] 请参阅destroyed() and QPointer 10.信号,当对象销毁时发生 void QObject::destroyed ( QObject * obj = 0 ) [signal] 请参阅deleteLater() 和QPointer类 11.解除信号和槽的连接 bool QObject::disconnect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method ) [static] 他一般有3种用法 a.解除一个类的所有信号的连接 CODE: disconnect(myObject, 0, 0, 0); 或者 disconnect(myObject, 0, 0, 0); [Copy to clipboard] b.解除一个特定的信号 CODE: disconnect(myObject, SIGNAL(mySignal()), 0, 0); 或者 myObject->disconnect(SIGNAL(mySignal())); [Copy to clipboard] c.解除一个特定的接受对象 CODE: disconnect(myObject, 0, myReceiver, 0); 或者 myObject->disconnect(myReceiver); [Copy to clipboard] 0就代表"any signal","any receiving object","any slot in the receiving object",就是所有信号,所有接收对象,一个接收对象的所有槽 注意:第一个参数就是sender不能为0,因为你不能解除所有信号和槽的关系 请参阅connect() 12.disconnect的函数2 bool QObject::disconnect ( const char * signal = 0, const QObject * receiver = 0, const char * method = 0 ) 13.disconnect的函数3 bool QObject::disconnect ( const QObject * receiver, const char * method = 0 ) 14.解除检测 void QObject::disconnectNotify ( const char * signal ) [virtual protected] 请参阅disconnect() 和connectNotify() 15.显示对象信息,一般用来调试用 void QObject::dumpObjectInfo () 请参阅dumpObjectTree() 16.显示对象树 void QObject::dumpObjectTree () 请参阅dumpObjectInfo() 17.检测事件,如果事件是经过验证或者执行过就返回true,否则false bool QObject::event ( QEvent * e ) [virtual] 请参阅installEventFilter(), timerEvent(), QApplication::sendEvent(), QApplication::postEvent(), 和QWidget::event() 18.事件过滤 bool QObject::eventFilter ( QObject * watched, QEvent * event ) [virtual] 例子: CODE: class MainWindow : public QMainWindow { public: MainWindow(); protected: bool eventFilter(QObject *obj, QEvent *ev); private: QTextEdit *textEdit; }; MainWindow::MainWindow() { textEdit = new QTextEdit; setCentralWidget(textEdit); textEdit->installEventFilter(this); } bool MainWindow::eventFilter(QObject *obj, QEvent *event) { if (obj == textEdit) { if (event->type() == QEvent::KeyPress) { qDebug("Ate key press"); return true; } else { return false; } } else { // pass the event on to the parent class return QMainWindow::eventFilter(obj, event); } } [Copy to clipboard] 在这个例子中只响应textEdit的事件 请参阅installEventFilter() 19.找出子类 T QObject::findChild ( const QString & name = QString() ) const 没有找到返回0,常用的调用 a.找特定的子类 CODE: QPushButton *button = parentWidget->findChild<QPushButton *>("button1"); [Copy to clipboard] b.找所有的同一类别子类 CODE: QListWidget *list = parentWidget->findChild<QListWidget *>(); [Copy to clipboard] 在VC6上,这个函数请用qFindChild()代替~~请参阅findChildren() 和qFindChild() 20.找出子类2,请参阅上例 QList<T> QObject::findChildren ( const QString & name = QString() ) const 21.找出子类3,请参阅上例 QList<T> QObject::findChildren ( const QRegExp & regExp ) const 22.判断继承关系,是true,否false bool QObject::inherits ( const char * className ) const 例子: CODE: QTimer *timer = new QTimer; // QTimer inherits QObject timer->inherits("QTimer"); // returns true timer->inherits("QObject"); // returns true timer->inherits("QAbstractButton"); // returns false // QLayout inherits QObject and QLayoutItem QLayout *layout = new QLayout; layout->inherits("QObject"); // returns true layout->inherits("QLayoutItem"); // returns false [Copy to clipboard] 请参阅metaObject() 和qobject_cast() 23.安装事件过滤 void QObject::installEventFilter ( QObject * filterObj ) 请参阅 removeEventFilter(), eventFilter(), 和event(). 24.判断一个类是不是Widget组件 bool QObject::isWidgetType () const 25.删除定时器 void QObject::killTimer ( int id ) 26.得到对象元信息 const QMetaObject * QObject::metaObject () const [virtual] 27.移动到某一线程 void QObject::moveToThread ( QThread * targetThread ) 请参阅thread() 28.得到父类 QObject * QObject::parent () const 请参阅setParent() 和children() 29.得到属性值 QVariant QObject::property ( const char * name ) const 请参阅setProperty(), QVariant::isValid(), 和metaObject() 30.得到一个信号的连接数 int QObject::receivers ( const char * signal ) const [protected] 31.删除事件监控 void QObject::removeEventFilter ( QObject * obj ) 请参阅installEventFilter(), eventFilter(), 和event() 32.得到信号的发送者 QObject * QObject::sender () const [protected] 33.设置父类 void QObject::setParent ( QObject * parent ) 请参阅 parent() 和QWidget::setParent(). 34.设置属性值 bool QObject::setProperty ( const char * name, const QVariant & value ) 请参阅property() and metaObject() 35.开始一个定时器 int QObject::startTimer ( int interval ) 失败返回0,注意这个参数依赖操作系统的时间 请参阅timerEvent(), killTimer(), and QTimer::singleShot(). 36.得到些类的线程 QThread * QObject::thread () const 请参阅moveToThread(). 37.计时器事件 void QObject::timerEvent ( QTimerEvent * event ) [virtual protected] 请参阅startTimer(), killTimer(), event() 38.翻译函数,关于这个函数的更多描述请参阅本网站国际化变成这一栏目 QString QObject::tr ( const char * sourceText, const char * comment ) [static] 请参阅trUtf8(), QApplication::translate(), and Internationalization with Qt. 39.翻译函数2 QString QObject::trUtf8 ( const char * sourceText, const char * comment ) [static] QObject宏 1.Q_CLASSINFO ( Name, Value ),他设置的东西可以在 QObject::metaObject()中找到 例子 CODE: class MyClass : public QObject { Q_OBJECT Q_CLASSINFO("Author", "Pierre Gendron") Q_CLASSINFO("URL", "[url]http://www.my-organization.qc.ca[/url]") public: ... }; [Copy to clipboard] 2.Q_ENUMS ( ... ) CODE: Q_ENUMS(Option AlignmentFlag EditMode TransformationMode) [Copy to clipboard] 更多请参阅Qt's Property System 3.Q_FLAGS ( ... ) CODE: Q_FLAGS(Options Alignment) [Copy to clipboard] 更多请参阅Qt's Property System 4.Q_INTERFACES ( ... )接口生命,一般用于写插件 如 CODE: class BasicToolsPlugin : public QObject, public BrushInterface, public ShapeInterface, public FilterInterface { Q_OBJECT Q_INTERFACES(BrushInterface ShapeInterface FilterInterface) public: ... }; [Copy to clipboard] 5.Q_OBJECT,略 6.Q_PROPERTY ( ... ) 他用来声明一个类的属性,语法如下 CODE: Q_PROPERTY(type name READ getFunction [WRITE setFunction] [RESET resetFunction] [DESIGNABLE bool] [SCRIPTABLE bool] [STORED bool]) [Copy to clipboard] 例子: CODE: Q_PROPERTY(QString title READ title WRITE setTitle) |
---|
编辑
楼主发表于: 2010-06-30 11:39
|
()
|
QObject是Qt类体系的唯一基类,就象MFC中的CObject和Dephi中的TObject,是Qt各种功能的源头活水,因此Qt源码分析的第一节就放在这个QObject上
int size = sizeof(QObject); QObject的大小是8,除了虚函数表指针需要的4个字节以外,另外的4个字节是: QObjectData *d_ptr; QObject中的数据被封装在QObjectData类中了,为什么要封装数据呢? 原因是Qt中有一个很重要的设计模式就是句柄实体模式,也就是以QObject为基类的类一般都是句柄类,一般只有一个指针指向一个实体类,在实体类中保存全部的数据,而且一般情况下这个指针还是私有的,方便以后修改句柄类的实现细节。 因此,也可以说和句柄类继承关系平行的也有一套实体类派生体系,因此,准确的说,Qt的基类其实有两个,一个是QObject,这是句柄类的唯一基类,另一个是QObjectData,这是实体类的基类 QObjectData类定义如下: class QObjectData { public: virtual ~QObjectData() = 0; QObject *q_ptr; QObject *parent; QObjectList children; uint isWidget : 1; uint pendTimer : 1; uint blockSig : 1; uint wasDeleted : 1; uint ownObjectName : 1; uint sendChildEvents : 1; uint receiveChildEvents : 1; uint unused : 25; int postedEvents; #ifdef QT3_SUPPORT int postedChildInsertedEvents; #else int reserved; #endif }; QObject *q_ptr; 这个指针指向实体类对应的句柄类,这和上面的代码 QObjectData *d_ptr; 遥相呼应,使得句柄类和实体类可以双向的引用,为什么是这样的命名方式呢?可能q指的是Qt接口类,d指的是Data数据类,这当然是猜测了,但是或许可以方便你记忆,在Qt中,这两个指针名字是非常重要的,必须记住。 但是仅仅如此还是不容易使用这两个指针,因为它们都是基类的类型,难道每次使用都要类型转换吗?为了简单起见,Qt在这里声明了两个宏 #define Q_DECLARE_PRIVATE(Class) / inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(d_ptr); } / inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(d_ptr); } / friend class Class##Private; #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; 只要在类的头文件中使用这两个宏,就可以通过函数直接得到实体类和句柄类的实际类型了,而且这里还声明了友元,使得数据类和句柄类连访问权限也不用顾忌了。 而且为了cpp文件中调用的方便,更是直接声明了以下两个宏 #define Q_D(Class) Class##Private * const d = d_func() #define Q_Q(Class) Class * const q = q_func() 好了,使用起来倒是方便了,但是以后局部变量可千万不能声明为d和q了(d,q分别代表了只想实体类对象和句柄类对象的常指针) 这里的d_func和q_func函数是非常常用的函数,可以理解为一个是得到数据类,一个是得到Qt接口类 QObject *parent; 这里指向QObject的父类 QObjectList children; 这里指向QObject相关的子类列表 这确实是个大胆的设计,如果系统中产生了1000000个QObject实例(对于大的系统,这个数字很容易达到吧),每个QObject子类平均下来是100(这个数字可能大了), 光这些指针的开销就有1000000*100*4=400M,是够恐怖的,如果我们必须在灵活性和运行开销之间做一个选择的话,无疑Qt选择了前者,对此我也很难评论其中的优劣, 还是祈求越来越强的硬件水平和Qt这么多年来得到的赫赫威名保佑我们根本就没有这个问题吧,呵呵 总之,Qt确实在内存中保存了所有类实例的树型结构 uint isWidget : 1; uint pendTimer : 1; uint blockSig : 1; uint wasDeleted : 1; uint ownObjectName : 1; uint sendChildEvents : 1; uint receiveChildEvents : 1; uint unused : 25; 这些代码就简单了,主要是一些标记位,为了节省内存开销,这里采用了位域的语法,还保留了25位为unused,留做以后的扩充 #ifdef QT3_SUPPORT int postedChildInsertedEvents; #else int reserved; #endif 这里或许是为了兼容Qt3下序列化的数据吧,即使没有定义QT3_SUPPORT,还是保留了一个数据reserved,以保证整个QObjectData的大小不变 具体看一个例子吧,对这种句柄实体模式加深认识,这就是Qt中的按钮类QPushButton QPushButton的句柄类派生关系是: QObject QWidget QAbstractButton QPushButton QPushButton的实体类派生关系是: QObjectData QObjectPrivate QWidgetPrivate QAbstractButtonPrivate QPushButtonPrivate 可以看出,这里确实是一个平行体系,只不过实体类派生关系中多了一个QObjectPrivate,这个类封装了线程处理,信号和槽机制等具体的实现,可以说它才是Qt实体类中真正起作用的基类,而QObjectData不过是一层浅浅的数据封装而已。 先不忙了解QObjectPrivate类中的接口和实现,我们先看看在Qt中,句柄类和实体类这两条体系是如何构造的? QPushButton* quit = new QPushButton("Quit"); 创建一个Qt的按钮,简简单单一行代码,其实背后大有玄机 QPushButton::QPushButton(const QString &text, QWidget *parent) : QAbstractButton(*new QPushButtonPrivate, parent) 首先QPushButton的构造函数中调用了QAbstractButton的构造函数,同时马上new出来一个QPushButtonPrivate实体类,然后把指针转换为引用传递给QAbstractButton QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent) : QWidget(dd, parent, 0) QAbstractButton的构造函数中继续调用基类QWidget的构造函数,同时把QPushButtonPrivate实体类指针继续传给基类 QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WFlags f) : QObject(dd, ((parent && (parent->windowType() == Qt::Desktop)) ? 0 : parent)), QPaintDevice() QWidget继续坐着同样的事情 QObject::QObject(QObjectPrivate &dd, QObject *parent) : d_ptr(&dd) 终于到了基类QObject,这里就直接把QPushButtonPrivate的指针赋值给了d_ptr(还记得这个变量名称吧) 最终在QPushButton构造时同时产生的new QPushButtonPrivate被写到了QObject中的d_ptr中 QObject::QObject(QObjectPrivate &dd, QObject *parent) : d_ptr(&dd) { Q_D(QObject); ::qt_addObject(d_ptr->q_ptr = this); QThread *currentThread = QThread::currentThread(); d->thread = currentThread ? QThreadData::get(currentThread)->id : -1; Q_ASSERT_X(!parent || parent->d_func()->thread == d->thread, "QObject::QObject()", "Cannot create children for a parent that is in a different thread."); if (parent && parent->d_func()->thread != d->thread) parent = 0; if (d->isWidget) { if (parent) { d->parent = parent; d->parent->d_func()->children.append(this); } // no events sent here, this is done at the end of the QWidget constructor } else { setParent(parent); } } 然后执行QObject的构造函数,这里主要是一些线程的处理,先不理它 QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WFlags f) : QObject(dd, ((parent && (parent->windowType() == Qt::Desktop)) ? 0 : parent)), QPaintDevice() { d_func()->init((parent && parent->windowType() == Qt::Desktop ? parent : 0), f); } 然后是QWidget的构造函数,这里调用了数据类QWidgetPrivate的init函数,这个函数不是虚函数,因此静态解析成QWidgetPrivate的init函数调用 QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent) : QWidget(dd, parent, 0) { Q_D(QAbstractButton); d->init(); } 然后是QAbstractButton的构造函数,这里调用了数据类QAbstractButton的init函数,这个函数不是虚函数,因此静态解析成QAbstractButton的init函数调用 QPushButton::QPushButton(const QString &text, QWidget *parent) : QAbstractButton(*new QPushButtonPrivate, parent) { Q_D(QPushButton); d->init(); setText(text); } 然后是QPushButton的构造函数,这里调用了数据类QPushButton的init函数,这个函数不是虚函数,因此静态解析成QPushButton的init函数调用 现在的事情很清楚了,总结一下: QPushButton在构造的时候同时生成了QPushButtonPrivate指针,QPushButtonPrivate创建时依次调用数据类基类的构造函数 QPushButton的构造函数中显示的调用了基类的构造函数并把QPushButtonPrivate指针传递过去,QPushButton创建时依次调用接口类基类的构造函数 在接口类的构造函数中调用了平行数据类的init函数,因为这个函数不是虚函数,因此就就是此次调用了数据类的init函数 需要指出的是,为什么QPushButtonPrivate实体类指针要转换为引用呢?为什么不是直接传递指针?结论是人家喜欢这样写,就是不传指针传引用,而且要用一个*new之类的怪异语法, 真叫人没有办法,其实这里用指针是一样的,代码看起来也自然一些. delete quit; 说完了构造,再说说析构 QPushButton::~QPushButton() { } 这里当然会调用QPushButton的析构函数了 QAbstractButton::~QAbstractButton() { #ifndef QT_NO_BUTTONGROUP Q_D(QAbstractButton); if (d->group) d->group->removeButton(this); #endif } 然后是QAbstractButton的析构函数 QWidget::~QWidget() { Q_D(QWidget); ... } 然后是QWidget的析构函数,这里洋洋洒洒一大堆代码,先不管它 QObject::~QObject() { ... } 最后是QObject的析构函数,这里也是洋洋洒洒的一大堆 Q_D(QObject); if (d->wasDeleted) { #if defined(QT_DEBUG) qWarning("Double QObject deletion detected"); #endif return; } d->wasDeleted = true; 这些没有什么好说的,就是设一个wasDeleted的标志,防止再被引用,对于单线程情况下,马上就要被删除了,还搞什么标记啊,根本没用,但是对于多线程情况下,这个标记应该是有用的 // set all QPointers for this object to zero GuardHash *hash = ::guardHash(); if (hash) { QWriteLocker locker(guardHashLock()); GuardHash::iterator it = hash->find(this); const GuardHash::iterator end = hash->end(); while (it.key() == this && it != end) { *it.value() = 0; it = hash->erase(it); } } 这里是支持QPointers的实现代码,我们以后再说 emit destroyed(this); Qt的一个指针删除时要发送destroyed信号,一般情况下是没有槽来响应的 QConnectionList *list = ::connectionList(); if (list) { QWriteLocker locker(&list->lock); list->remove(this); } 这里清除了信号槽机制中的记录 if (d->pendTimer) { // have pending timers QThread *thr = thread(); if (thr || d->thread == 0) { // don't unregister timers in the wrong thread QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance(thr); if (eventDispatcher) eventDispatcher->unregisterTimers(this); } } 这里清除定时器 d->eventFilters.clear(); 这里清除事件过滤机制 // delete children objects if (!d->children.isEmpty()) { qDeleteAll(d->children); d->children.clear(); } 这里清除所有子类指针,当然每个子类指针清除时又会清除它的所有子类,因此Qt中new出来的指针很少有显示对应的delete,因为只要最上面的指针被框架删除了, 它所连带的所有子类都被自动删除了 { QWriteLocker locker(QObjectPrivate::readWriteLock()); ::qt_removeObject(this); /* theoretically, we cannot check d->postedEvents without holding the postEventList.mutex for the object's thread, but since we hold the QObjectPrivate::readWriteLock(), nothing can go into QCoreApplication::postEvent(), which effectively means noone can post new events, which is what we are trying to prevent. this means we can safely check d->postedEvents, since we are fairly sure it will not change (it could, but only by decreasing, i.e. removing posted events from a differebnt thread) */ if (d->postedEvents > 0) QCoreApplication::removePostedEvents(this); } if (d->parent) // remove it from parent object d->setParent_helper(0); delete d; d_ptr = 0; 这里要删除相关的数据类指针了 |
---|