zoukankan      html  css  js  c++  java
  • 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关键字。

  • 相关阅读:
    [转]Design Time Serialization
    啥都不说了,不枉熬油点灯了
    Eclips汉化
    FreeBSD 上用上苹果黑体,效果很棒
    使用Portupgrade
    fvwm2rc
    make.conf 配置优化
    基于FreeBSD5.4全能服务器安装(dns,ftp,apache,qmail)
    portssupfile
    关于FreeBSD 5优化的补充
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/11026850.html
Copyright © 2011-2022 走看看