对象树和拥有权

一、对象模型

标准C++对象模型可以在运行时非常有效的支持对象范式(object paradigm),但是它的静态特性在一些问题领域中不够灵活。图形用户界面编程不仅需要运行时的高效性,还需要高度的灵活性。为此,Qt在标准C++对象模型的基础上添加了一些特性,形成了自己的对象模型。这些特性有:

  • 一个强大的无缝对象通信机制——信号和槽(signals and slots);
  • 可查询和可设计的对象属性系统(object properties);
  • 强大的事件和事件过滤器(events and event filters);
  • 通过上下文进行国际化的字符串翻译机制(string translation for internationalization);
  • 完善的定时器(timers)驱动,使得可以在一个事件驱动的GUI中处理多个任务;
  • 分层结构的、可查询的对象树(object trees),它使用一种很自然的方式来组织对象拥有权(object ownership);
  • 守卫指针即QPointer,它在引用对象被销毁时自动将其设置为0;
  • 动态的对象转换机制(dynamic cast);

Qt的这些特性都是在遵循标准C++规范内实现的,使用这些特性都必须要继承自QObject类。其中对象通信机制和动态属性系统,还需要元对象系统(Meta-ObjectSystem)的支持。关于对象模型的介绍,大家可以在帮助中查看Object Model关键字。

二、元对象系统

Qt中的元对象系统(Meta-Object System)提供了对象间通信的信号和槽机制、运行时类型信息和动态属性系统。元对象系统是基于以下三个条件的:

  • 该类必须继承自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::newInstance()构造该类的一个新实例。

除了这些特性外,还可以使用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关键字。

三、对象树与拥有权

Qt中使用对象树(object tree)来组织和管理所有的QObject类及其子类的对象。当创建一个QObject时,如果使用了其他的对象作为其父对象(parent),那么这个QObject就会被添加到父对象的children()列表中,这样当父对象被销毁时,这个QObject也会被销毁。实践表明,这个机制非常适合于管理GUI对象。例如,一个QShortcut(键盘快捷键)对象是相应窗口的一个子对象,所以当用户关闭了这个窗口时,这个快捷键也可以被销毁。

QWidget作为能够在屏幕上显示的所有部件的基类,扩展了对象间的父子关系。一个子对象一般也就是一个子部件,因为它们要显示在父部件的区域之中。例如,当关闭一个消息对话框(message box)后要销毁它时,消息对话框中的按钮和标签也会被销毁,这也正是我们所希望的,因为按钮和标签是消息对话框的子部件。当然,我们也可以自己来销毁一个子对象。关于这一部分的内容,大家可以在帮助索引中查看Object Trees &Ownership关键字。

在前面的Qt编程中我们应该看到过很多使用new来创建一个部件,但是却没有使用delete来进行释放的问题。这里再来研究一下这个问题。

新建Qt Gui应用,项目名称为“myOwnership”,基类选择QWidget,然后类名保持“Widget”不变。完成后向项目中添加新文件,模板选择C++ Class,类名为“MyButton”,基类为“QPushButton”,类型信息选择“继承自QWidget”。添加完文件后在mybutton.h文件中添加析构函数的声明:

~MyButton();

然后到mybutton.cpp文件中添加头文件#include <QDebug>并定义析构函数:

MyButton::~MyButton()

{

qDebug() << "delete button";

}

这样当MyButton的对象被销毁时,就会输出相应的信息。这里定义析构函数,只是为了更清楚的看到部件的销毁过程,其实一般在构建新类时不需要实现析构函数。下面在widget.cpp文件中进行更改,添加头文件:

#include "mybutton.h"

#include<QDebug>

在构造函数中添加代码:

MyButton *button = new MyButton(this); // 创建按钮部件,指定widget为父部件

button->setText(tr("button"));

更改析构函数:

Widget::~Widget()

{

delete ui;

qDebug() << "delete widget";

}

Widget类的析构函数中默认的已经有了销毁ui的语句,这里又添加了输出语句。当Widget窗口被销毁时,将输出信息。下面运行程序,然后关闭窗口,在QtCreator的应用程序输出栏中的输出信息为:

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操作符在堆上进行创建,不过一定要指定其父部件,这样就不需要再使用delete操作符来销毁该对象了。

还有一种重定义父部件(reparented)的情况,例如,将一个包含其他部件的布局管理器应用到窗口上,那么该布局管理器和其中的所有部件都会自动将它们的父部件转换为该窗口部件。在widget.cpp文件中添加头文件#include <QHBoxLayout>,然后在构造函数中继续添加代码:

MyButton *button2 = new MyButton;

MyButton *button3 = new MyButton;

QHBoxLayout *layout = new QHBoxLayout;

layout->addWidget(button2);

layout->addWidget(button3);

setLayout(layout);      // 在该窗口中使用布局管理器

这里创建了两个MyButton和一个水平布局管理器,但是并没有指定它们的父部件,现在各个部件的拥有权(ownership)不是很清楚。但是当使用布局管理器来管理这两个按钮,并且在窗口中使用这个布局管理器后,这两个按钮和水平布局管理器都将重定义父部件而成为窗口Widget的子部件。可以使用children()函数来获取一个部件的所有子部件的列表,例如在构造函数中再添加如下代码:

qDebug() << children();    // 输出所有子部件的列表

这时大家可以运行一下程序,查看应用程序输出栏中的信息,然后根据自己的想法更改一下程序,来进一步体会Qt中对象树的概念。

四、总结

  Qt中的对象树很好地解决了父子部件的关系,对于Gui编程是十分方便的,在创建部件时我们只需要关注它的父部件,这样就不用再考虑其销毁问题了。下一节,我们将讲解Qt中的信号和槽的内容。

时间: 2024-11-08 22:16:18

对象树和拥有权的相关文章

Qt对象模型之二:对象树与元对象系统

一.对象树的概念 Qt中使用对象树(object tree)来组织和管理所有的QObject类及其子类的对象.当创建一个QObject时,如果使用了其他的对象作为其父对象(parent),那么这个 QObject就会被添加到父对象的children()列表中,这样当父对象被销毁时,这个QObject也会被销毁.实践表明,这个机制非常适合于管理GUI对象.例如,一个 QShortcut(键盘快捷键)对象是相应窗口的一个子对象,所以当用户关闭了这个窗口 时,这个快捷键也可以被销毁. QWidget作

IE与FireFox的DOM对象树差异

 最近在做项目,有复习一下以前JS的关于DOM的知识点,再次小小的总结一下. 以下是DOM对象在IE与FireFox中的一些小区别,就当积累吧. 1.IE会把没有在文档中定义的属性也加入DOM树 2.IE不会把title中的文本内容加入DOM树 3.IE会把换行缩进信息过滤掉,firefox则会认为是有用的文本内容,并作为文本节点的一部分加入DOM树 4.IE不会把Script标签中的内容加入DOM树,FireFox将里面的内容加入DOM树

Qt, 我回来了。。。

说起qt,大学时就有接触,但一直没有深入,这个周六周天利用两于时间重新温习了一下,跟之前用过的vs上的MFC.C++ builder比起来,Qt封装很人性化,库也比较全,写个 一般的小工具很轻松. 参考的教程为:Qt快速入门系列教程目录 , 很详细很实用(除了一些插图说明看不了) 导语        该系列教程是基于QtCreator开发环境的Qt入门级教程.自2009年10月至今的两年多时间里,该系列教程逐渐完善,已经包含了Qt基础.2D绘图.数据库和XML.网络编程.Qt Quick等最基本

Lambda表达式和表达式树

在C# 2.0中,通过方法组转换和匿名方法,使委托的实现得到了极大的简化.但是,匿名方法仍然有些臃肿,而且当代码中充满了匿名方法的时候,可读性可能就会受到影响.C# 3.0中出现的Lambda表达式在不牺牲可读性的前提下,进一步简化了委托. LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态.这些操作表示了各种关于数据的逻辑,例如数据筛选,数据排序等等.通常这些操作都是用委托来表示.Lambda表达式是对LINQ数据操作的一种符合语言习惯的表示方式. Lambda表达式不仅可以用来创

第16课 Qt对象间的父子关系

1. Qt对象间的关系 (1)Qt对象间可以存在父子关系 ①每一个对象都保存有它所有子对象的指针 ②每一个对象都有一个指向其父对象的指针 (2)当指定Qt对象的父对象时 ①其父对象会在子对象链表中加入该对象的指针 ②该对象会保存指向其父对象的指针 (3)当Qt对象被销毁时 ①将自己从父对象的Children List移除 ②将自己的Children List中的所有对象销毁 ▲使用Qt开发时,不仅要时刻注意内存泄露的问题,还要时刻关注对象是否可能被多次销毁的问题. [编程实验]对象间的父子关系

XML编程总结(六)——使用JAXB进行java对象和xml格式之间的相互转换

(六)使用JAXB进行java对象和xml格式之间的相互转换 JAXB能够使用Jackson对JAXB注解的支持实现(jackson-module-jaxb-annotations),既方便生成XML,也方便生成JSON,这样一来可以更好的标志可以转换为JSON对象的JAVA类. JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术.该过程中,JAXB也提供了将XML实例文档反向生成Java对象树

#8.10.16总结# 属性选择符 伪对象选择符 CSS的常用样式

属性选择符 E[att] E[att="val"] E[att~="val"] E[att^="val"] E[att$="val"] E[att*="val"] E[att|="val"] 伪对象选择符  E:first-letter/E::first-letter 设置对象内的第一个字符的样式. <p>今天,阳光明媚,晴空万里,非常适合户外活动,如踏青.远足之类的.长期坐

10,组合模式(Composite Pattern)是将对象组合成树形结构以表示“部分--整体”的层次结构。使得用户对单个对象和组合对象的使用具有一致性。

Composite模式也叫组合模式,是构造型的设计模式之一.通过递归手段来构造树形的对象结构,并可以通过一个对象来访问整个对象树. Component (树形结构的节点抽象) - 为所有的对象定义统一的接口(公共属性,行为等的定义) - 提供管理子节点对象的接口方法 - [可选]提供管理父节点对象的接口方法  Leaf (树形结构的叶节点) Component的实现子类  Composite(树形结构的枝节点) Component的实现子类 适用于: 单个对象和组合对象的使用具有一致性.将对象组

C#中的Lambda表达式和表达式树

在C# 2.0中,通过方法组转换和匿名方法,使委托的实现得到了极大的简化.但是,匿名方法仍然有些臃肿,而且当代码中充满了匿名方法的时候,可读性可能就会受到影响.C# 3.0中出现的Lambda表达式在不牺牲可读性的前提下,进一步简化了委托. LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态.这些操作表示了各种关于数据的逻辑,例如数据筛选,数据排序等等.通常这些操作都是用委托来表示.Lambda表达式是对LINQ数据操作的一种符合语言习惯的表示方式. Lambda表达式不仅可以用来创