Qt對象模型之二:對象樹與元對象系統


一、對象樹的概念

Qt中使用對象樹(object tree)來組織和管理所有的QObject類及其子類的對象。當創建一個QObject時,如果使用了其他的對象作為其父對象(parent),那么這個 QObject就會被添加到父對象的children()列表中,這樣當父對象被銷毀時,這個QObject也會被銷毀。實踐表明,這個機制非常適合於管理GUI對象。例如,一個 QShortcut(鍵盤快捷鍵)對象是相應窗口的一個子對象,所以當用戶關閉了這個窗口 時,這個快捷鍵也可以被銷毀。

QWidget作為能夠在屏幕上顯示的所有部件的基類,擴展了對象間的父子關系。 一個子對象一般也就是一個子部件,因為它們要顯示在父部件的區域之中。例如,當關閉一個消息對話框(message box)后要銷毀它時,消息對話框中的按鈕和標簽也會被銷毀,這也正是我們所希望的,因為按鈕和標簽是消息對話框的子部件。當然,也可以自己來銷毀一個子對象。關於這一部分內容,可以在幫助索引中査看Object Trees & Ownership關鍵字。



二、對象樹的示例程序

新建Qt Gui應用,項目名稱為myOwnership,基類選擇QWidget,然后類名保持Widget不變。完成后向項目中添加新文件,模板選擇C+ +類,類名為MyButton,基類為QPushButton,類型信息選擇“繼 承自QWidget”。添加完文件后將mybuuon. h文件修改如下:

#ifndef MYBUTTON_H
#define MYBUTTON_H

#include <QPushButton>
#include <QDebug>

class MyButton : public QPushButton
{
    Q_OBJECT
public:
    explicit MyButton(QWidget *parent = nullptr);
    ~MyButton();
};

#endif // MYBUTTON_H

這里主要是添加了析構函數的聲明。然后到mybutton. cpp文件中,修改如下:

#include "mybutton.h"

MyButton::MyButton(QWidget *parent) :
    QPushButton(parent)
{
}

MyButton::~MyButton()
{
    qDebug() << "delete button";
}

這里添加了析構函數的定義,這樣當 MyButton 的對象被銷毀時,就會輸出相應的信息。



下面到widget.cpp文件中,修改如下:

#include "widget.h"
#include "ui_widget.h"
#include "mybutton.h"
#include <QDebug>
#include <QHBoxLayout>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    //創建按鈕部件,指定widget為父部件
    MyButton *button = new MyButton(this);
    button->setText(tr("button"));
}

Widget::~Widget()
{
    delete ui;
    qDebug() << "delete widget";
}

當Widget窗口被銷毀時,將輸出信息。下面運行程序,然后關閉窗口,在Qt Creator的應用程序輸出欄中的輸出信息為:

delete widget
delete button

可以看到,當關閉窗口后,因為該窗口是頂層窗口,所以應用程序要銷毀該窗口部件(如果不是頂層窗口,那么關閉時只是隱藏,不會被銷毀),而當窗口部件銷毀時會自動銷毀其子部件。這也就是為什么在Qt中經常只看到new操作而看不到delete操作 的原因。



再來看一下main.cpp文件,其中Widget對象是建立在棧上的:

Widget w;
w.show();

這樣對於對象w,在關閉程序時會自動銷毀。而對於Widget中的部件,如果是在堆上創建(使用new操作符),那么只要指定Widget為其父窗口就可以了,也不需要進行delete操作。當對象w銷毀時會自動銷毀它的所有子部件,這些都是Qt的對象樹所完成的。

所以,對於規范的Qt程序,我們要在main()函數中將主窗口部件創建在棧上,例如“Widget w;”,而不要在堆上進行創建(使用new操作符)。對於其他窗口部件,可以使用new操作符在堆上進行創建,不過一定要指定其父部件,這樣就不用使用de­lete操作符來銷毀該對象了。



三、元對象系統的概念

Qt中的元對象系統(Meta-Object System)提供了對象間通信的信號和槽機制、運行時類型信息和動態屬性系統。元對象系統是基於以下3個條件的:

  • 該類必須繼承自QObject類;
  • 必須在類的私有聲明區聲明Q_OBJECT宏(在類定義時,如果沒有指定public或者private,則默認為private);
  • 元對象編譯器Meta-Object Compiler(moc),為QObject的子類實現元對象特性提供必要的代碼。

其中,moc工具讀取一個C+ +源文件,如果它發現一個或者多個類的聲明中包含有Q_OBJECT宏,便會另外創建一個C+ +源文件(就是在項目目錄中的debug目錄 下看到的以moc開頭的C+ +源文件),其中包含了為每一個類生成的元對象代碼。 這些產生的源文件或者被包含進類的源文件中,或者和類的實現同時進行編譯和鏈接。



元對象系統主要是為了實現信號和槽機制才被引入的,不過除了信號和槽機制以外,元對象系統還提供了其他一些特性:

  • QObjeCt::metaObject()函數可以返回一個類的元對象,它是QMetaObject類的對象;
  • QMetaObject::className()可以在運行時以字符串形式返回類名,而不需要C+ +編輯器原生的運行時類型信息(RTTI)的支持;
  • QObject:: “inherits()函數返回一個對象是否是QObject繼承樹上一個類的實例的信息;
  • QObject: :tr()和QObject: :trUtf8()迸行字符串翻譯來實現國際化;
  • QObject::setProperty()和QObject::property()通過名字來動態設置或者獲取對象屬性;
  • QMetaObject: :newlnstance()構造該類的一個新實例。


除了這些特性,還可以使用qobject_cast()函數來對QObject類進行動態類型轉換,這個函數的功能類似於標准C+ +中的dynamic_cast()函數,但它不再需要RTTI的支持。這個函數嘗試將它的參數轉換為尖括號中的類型的指針,如果是正確的類型,則返回一個非零的指針,如果類型不兼容則返回0。例如:

QObject *obj = new MyWidget;
QWidget *widget = qobject_cast<QWidget *>(obj);

信號和槽機制是Qt的核心內容,而信號和槽機制必須依賴於元對象系統,所以它是Qt中很關鍵的內容。這里只是說明了它的一些應用,關於它的具體實現機制,這里不再講述。關於元對象系統的具體描述,可以在Qt中查看The Meta Object System關鍵字。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM