浅析开源内存数据库Fastdb

介绍:

Fastdb是免费开源内存数据库,其优秀的性能,和简洁的C++代码,让我学习使用过程中收益颇多,但是国内中文相关研究的文章相当稀少,外文我查询相当不便。有兴趣的朋友可以通过以下网站访问:Main Memory Object-Relational Database Management System,相关文档详见:FastDB Main Memory Database Management System。本文将以Windows版本C++版本号3.76为基础进行开发和测试,听说有4.14的版本但是我没有办法获取,有获得新版本的朋友不妨能发我一份学习,我的邮箱:lyfwwb@163.com,在此先行感谢!本文将记录学习相关过程中心得和一些功能分析,希望自己不会忘记曾经使用过的历史和自己改造过程。借此机会给那些和我一样想用的朋友又碰到一样问题的人提供一些思路和方法。希望有人可以给我更好更完善的资料系统学习。很多人认为Fastdb只能本机上进行数据库操作,其实Fastdb在3.76版本中已经提供简单服务端/客户端功能了,有C++编程能力的可以实现更多的功能。框架作者已经做得很好了。

下面是Fastdb官方的介绍译文:

FastDB是一个高效的主存数据库系统,具有实时功能和方便的C++接口。FastDB不支持客户端-服务器体系结构,所有使用FastDB数据库的应用程序都应该在同一主机上运行。FastDB针对具有主导读取访问模式的应用程序进行了优化。通过消除数据传输开销和非常有效的锁定实现,可以提供高速的查询执行。数据库文件被映射到与数据库一起工作的每个应用程序的虚拟内存空间。因此,查询是在应用程序的上下文中执行的,不需要上下文切换和数据传输。并发数据库访问的同步是通过原子指令在FastDB中实现的,几乎不增加查询处理的开销。FastDB假设整个数据库都存在于RAM中,并根据这一假设优化搜索算法和结构。此外,FastDB没有数据库缓冲区管理引起的开销,并且不需要在数据库文件和缓冲池之间进行数据传输。这就是为什么FastDB的工作速度要比所有数据都缓存在缓冲池中的传统数据库快得多。

FastDB支持事务处理、在线备份和系统崩溃后的自动恢复。事务提交协议基于影子根页算法,执行数据库的原子更新。恢复可以非常快速地完成,为关键应用程序提供高可用性。此外,事务日志的消除提高了整个系统的性能,并使系统资源得到更有效的利用。

FastDB是一个面向应用程序的数据库。数据库表是使用有关应用程序类的信息构建的。FastDB支持自动方案评估,允许您只在一个地方进行更改——在应用程序类中。FastDB为从数据库中检索数据提供了一个灵活方便的接口。使用类似SQL的查询语言来指定查询。非原子字段、嵌套数组、用户定义的类型和方法、直接的对象间引用等后关系功能简化了数据库应用程序的设计,提高了它们的效率。

尽管FastDB是在假设数据库作为一个整体适合计算机的物理内存的情况下进行优化的,但也可以将其与数据库一起使用,因为数据库的大小超过了系统中物理内存的大小。在最后一种情况下,标准操作系统交换机制将起作用。但所有FastDB搜索算法和结构都是在所有数据都驻留在内存中的假设下进行优化的,因此交换数据的效率不会很高。

功能模块的分析与介绍

1、查询语言介绍:

FastDB支持具有类似SQL语法的查询语言。FastDB使用的符号在面向对象编程中比在关系数据库中更受欢迎。表行被视为对象实例,表是这些对象的类。与SQL不同,FastDB面向的是使用对象,而不是SQL元组。因此,每次查询执行的结果都是一个类的一组对象。FastDB查询语言与标准SQL的主要区别在于:

1、 没有几个表和嵌套子查询的联接。查询总是从一个表中返回一组对象。

2、 标准C类型用于原子表列。

3、 除了空引用之外,没有空值。我完全同意C.J.Date对三值逻辑的批评,以及他使用默认值的建议。

4、 结构和数组可以用作记录组件。提供了一个特殊的存在量化器来定位数组中的元素。

5、 可以为表记录(对象)以及记录组件定义无参数用户方法。

6、 应用程序可以定义(只有)一个字符串或数字参数的用户函数。

7、 支持对象之间的引用,包括对反向引用的自动支持。

8、 从后面开始的构造使用引用执行递归记录遍历。

9、 由于查询语言被深度集成到C++类中,因此对语言标识符和关键字都使用了区分大小写的模式。

10、 没有完成整数和浮点类型到字符串表示形式的隐式转换。如果需要这样的转换,就必须明确地进行。

下面以类似BNF的符号表示法规定了FastDB查询语言搜索谓词的语法:

标识符区分大小写,以a-z、a-z、'_'或'$'字符开头,仅包含a-z、a-z、0-9、'_‘或'$‘字符,并且不重复SQL保留字。

保留字列表 
absandascbetweenby
currentdescescapeexistsfalse
firstfollowfromininteger
islengthlikelastlower
matchnotnullorreal
startstringtrueupper

也可以使用ANSI标准注释。双连字符后直到行尾的所有字符都将被忽略。

FastDB通过支持位操作来扩展ANSI标准SQL操作。运算符and/or不仅可以应用于布尔操作数,还可以应用于整数类型的操作数。将and/or运算符应用于整数操作数的结果是通过位and/位or运算设置位的整数值。位操作可以用于小集合的有效实现。此外,对于整数和浮点类型,FastDB也支持对幂运算(x^y)的引用。

2、结构

FastDB接受结构作为记录的组成部分。可以使用标准点表示法访问结构的字段如 :company.address.city

结构字段可以按规范的顺序进行索引和使用。结构可以包含其他结构作为其组件;嵌套级别没有限制。

程序员可以定义结构的方法,这些方法可以在查询中使用与正常结构组件相同的语法。这样的方法除了指向它所属对象的指针(C++中的this指针)之外,不应该有任何参数,并且应该返回一个原子值(布尔值、数值、字符串或引用类型)。此外,该方法不应更改对象实例(不可变方法)。如果该方法返回一个字符串,则应该使用new char数组空间 来分配该字符串,因为临时变量将在复制其值后被删除。

因此,用户定义的方法可以用于创建虚拟组件——这些组件不存储在数据库中,而是使用其他组件的值进行计算。例如,FastDB dbDateTime类型仅包含整数时间戳组件和dbDateTime::year()、dbDateTime::month()等方法。。。因此,可以在应用程序中指定诸如“deliveryy.year=1999”之类的查询,其中交付记录字段具有dbDateTime类型。方法是在定义它们的应用程序上下文中执行的,其他应用程序和交互式SQL不可用。

3、数组

FastDB接受具有动态长度的数组作为记录的组成部分。不支持多维数组,但可以定义数组的数组。可以按数组字段的长度对结果集中的记录进行排序。FastDB提供了一组用于处理数组的特殊构造:

通过length()函数可以获得数组中元素的数量。

数组元素可以由[]运算符提取。如果索引表达式超出数组范围,将引发异常。

中的运算符可用于检查数组是否包含由左操作数指定的值。此操作只能用于原子类型的数组:具有布尔值、数值、引用或字符串组件。

数组可以使用update方法更新,该方法创建数组的副本并返回非常量引用。

数组元素的迭代由exists运算符执行。在exists关键字之后指定的变量可以用作exists量化器前面的表达式中数组的索引。此索引变量将遍历所有可能的数组索引值,直到表达式的值变为true或索引超出范围。条件

  exists i: (contract[i].company.location = 'US')

将选择位于“美国”的公司发货的所有详细信息,而查询

 not exists i: (contract[i].company.location = 'US')

将选择从“美国”以外的公司发货的所有详细信息。

允许嵌套的exists子句。使用嵌套的exists量化器相当于使用对应索引变量的嵌套循环。例如查询

    exists i: (contract[i].company.location = 'US')

将选择所有记录,在矩阵字段的元素中包含0,该字段的类型为array或array of integer。此构造等效于以下两个嵌套循环:

 bool result = false;
       for (int column = 0; column < matrix.length(); column++) { 
            for (int row = 0; row < matrix[column].length(); row++) { 
             if (matrix[column][row] == 0) { 
                     result = true;
             break;
                 }
            }
       }

使用索引的顺序至关重要!以下查询执行的结果

  exists row: (exists column: (matrix[column][row] = 0))

将与上一次查询的结果完全不同。在最后一种情况下,在空矩阵的情况下,程序只是由于无限循环而挂起。

4、字符串

FastDB中的所有字符串都有不同的长度,程序员不应该担心字符字段的最大长度的指定。数组可接受的所有操作也适用于字符串。除了它们之外,字符串还有一组自己的操作。首先,可以使用标准关系运算符将字符串相互比较。目前,FastDB只支持ASCII字符集(对应于C中的char类型)和忽略位置设置的逐字节字符串比较。

类似的运算符可用于将字符串与包含特殊通配符“%”和“_”的模式进行匹配。字符“_”与任何单个字符匹配,而字符“%”与零个或多个字符匹配。如果模式中的字符“%”和“_”前面有一个特殊的转义符(在转义关键字之后指定),则可以使用类似运算符的扩展形式和转义关键字将它们作为普通字符处理。

如果您使用USE_REGEX宏重建GigaBASE,那么您可以使用实现标准正则表达式的匹配运算符(基于GNU正则表达式库)。此运算符的第二个操作数指定了要匹配的正则表达式,并且应该是字符串文字。

可以通过in运算符搜索字符串中的子字符串。对于颜色字段包含“蓝色”的所有记录,表达式(颜色为“蓝色”)都将为true。如果搜索字符串的长度大于某个阈值(当前为512),则使用Boyer-Moore子字符串搜索算法,而不是直接的搜索实现。

字符串可以通过+或||运算符连接。最后一个是为了与ANSI SQL标准兼容而添加的。由于FastDB不支持在表达式中隐式转换为字符串类型,因此可以为字符串重新定义运算符+的语义。

Fastdb是一个面向对象的数据库系统,其主要目标是提供高效的、易于使用的数据存储和检索机制。本文将详细介绍Fastdb的特点和使用方法,并解释所有相关的接口。

一、特点

1. 高效性:Fastdb使用预分配对象的方式,减少了向磁盘分配空间的时间开销。它还使用了内存映射I/O技术,有效地减少了基于文件的I/O操作。

2. 安全性:Fastdb使用了事务机制,可以保证对数据的完整性。而且,它提供了可选的数据加密机制,以保护敏感数据。

3. 易于使用:Fastdb提供了面向对象的API,使得程序员可以快速、直接地存储和检索对象。同时,Fastdb提供了具有强大查询能力的索引机制,以便更高效地定位数据。

4. 可扩展性:Fastdb支持多种语言的接口,包括C++、Java、Python等,可以方便地与其他软件集成。

5. 易于部署:Fastdb是一个轻量级的数据库系统,可以很容易地安装和配置。

二、安装

Fastdb针对不同的操作系统提供了不同的安装包,可在其官方网站上下载。安装过程可以根据安装向导进行,也可以使用命令行方式安装。

三、使用

1. 创建数据库

在使用Fastdb之前,我们需要先创建一个数据库。可以通过以下代码创建并打开一个名为“MyDB”、大小为100MB的数据库:

```
  //创建数据库
  db_open("MyDB", 100000000);
  //获取根对象
  dbDatabase* db = dbDatabase::instance();
```

2. 定义对象

Fastdb的对象被定义为类,需要继承于dbObject类。例如,下面定义了一个Person类:

```
  class Person : public dbObject
  {
  public:
      char* name;
      int age;
      dbReference<Person> father;
      dbReference<Person> mother;
      dbArray<dbReference<Person> > children;
  };
```

在这个类中,我们定义了Person的姓名、年龄、父亲、母亲和孩子。dbReference表示一个对象引用,dbArray表示一个对象数组。dbObject类提供了一些常用的函数,例如dbObject::dbObject()、dbObject::~dbObject()、dbObject::clear()等。

3. 存储对象

我们可以使用dbDatabase::addObject()函数将一个新对象添加到数据库中,并返回一个dbReference对象。

```
  //创建一个新的Person对象并存储到数据库中
  Person* p = new Person;
  p->name = "Lucy";
  p->age = 25;
  dbReference<Person> ref = db->addObject(p);
```

4. 查询对象

Fastdb支持多种条件查询方式,可以快速地查找到我们需要的对象。例如,我们可以使用以下代码查找年龄为25岁的人:

```
  //查询年龄为25的人
  dbQuery q;
  q = db->createQuery();
  q.constrain(Person::age, opEqual, 25);
  q.execute();
  while (q.next()) {
      Person* p = q.object();
      cout << p->name << endl;
  }
```

其中,opEqual表示相等条件,还支持opLess、opGreater等。使用dbQuery::execute()函数执行查询,dbQuery::next()函数将游标移到下一个符合要求的对象位置,dbQuery::object()函数返回当前对象的指针。

5. 更新对象

我们可以使用dbDatabase::updateObject()函数更新已经存在的对象。例如,我们可以修改25岁的Lucy的名字:

```
  //修改Lucy的名字
  q = db->createQuery();
  q.constrain(Person::name, opEqual, "Lucy");
  q.constrain(Person::age, opEqual, 25);
  q.execute();
  while (q.next()) {
      Person* p = q.object();
      p->name = "Lucy Li";
      db->updateObject(p);
  }
```

6. 删除对象

我们可以使用dbDatabase::deleteObject()函数删除已经存在的对象。例如,我们可以删除所有年龄大于30岁的人:

```
  //删除年龄大于30的人
  q = db->createQuery();
  q.constrain(Person::age, opGreater, 30);
  q.execute();
  while (q.next()) {
      Person* p = q.object();
      db->deleteObject(p);
  }
```

四、接口解释

dbDatabase类

1. dbDatabase()

函数说明:构造函数,创建一个数据库对象。

2. static dbDatabase* instance()

函数说明:获取数据库的全局实例。

3. void open(const char* name, int size)

参数说明:name-数据库名称,size-数据库大小(字节)。

函数说明:打开一个数据库文件,并将它设置为当前数据库。

4. void close()

函数说明:关闭数据库文件。

5. dbReference<T> addObject(T* obj)

参数说明:obj-要添加的对象。

函数说明:将一个新对象添加到数据库末尾,并返回一个引用。

6. void updateObject(T* obj)

参数说明:obj-要更新的对象。

函数说明:将一个已经存在的对象更新到数据库中。

7. void deleteObject(T* obj)

参数说明:obj-要删除的对象。

函数说明:从数据库中删除一个已经存在的对象。

8. int commit()

函数说明:提交所有的事务。

9. void rollback()

函数说明:回滚所有的事务。

10. dbQuery createQuery()

函数说明:创建一个查询对象。

11. template <class T> dbReference<T> getReference(T* obj)

参数说明:obj-对象。

函数说明:获取一个对象的引用。

dbQuery类

1. dbQuery(dbDatabase* db)

参数说明:db-数据库对象。

函数说明:构造函数。

2. ~dbQuery()

函数说明:析构函数。

3. void close()

函数说明:关闭查询对象。

4. void reset()

函数说明:重置查询条件。

5. void constrain(dbFieldDescriptor* fd, dbComparisonOperator op, const void* value)

参数说明:fd-一个字段描述器,op-比较操作符,value-比较值。

函数说明:增加一个查询条件。

6. void orderBy(dbFieldDescriptor* fd, bool ascending=true)

参数说明:fd-一个字段描述器,ascending-是否升序排列。

函数说明:指定排序方式。

7. void select(const char* fieldName)

参数说明:fieldName-要返回的字段名称。

函数说明:指定要返回的字段。

8. bool execute()

函数说明:执行查询。

9. bool next()

函数说明:移动游标到下一个结果。

10. dbReference<T> object()

函数说明:返回当前游标所指向的对象引用。

dbFieldDescriptor类

1. dbFieldDescriptor(const char* name, int type, size_t length, int flags)

参数说明:name-字段名称,type-字段类型(int、float、double、char[]等),length-字段长度(对于char[]类型的字段有用),flags-字段属性(比如索引)。

函数说明:构造函数。

dbReference类

1. bool operator== (const dbReference& r)

参数说明:r-一个引用。

函数说明:判断两个引用是否相等。

2. bool operator!= (const dbReference& r)

参数说明:r-一个引用。

函数说明:判断两个引用是否不相等。

3. operator const T* ()

函数说明:将引用转换为指向T类型的指针。

dbArray类

1. dbArray<T>()

函数说明:构造函数。

2. void resize(int n, bool save=true)

参数说明:n-新数组大小,save-是否保留旧数据。

函数说明:重新调整数组大小。

3. void clear()

函数说明:清空数组内容。

4. void add(const T& item)

参数说明:item-要添加的项。

函数说明:将一个项添加到数组末尾。

5. T& operator[](int i) const

参数说明:i-数组下标。

函数说明:获取指定下标的项。

6. int length()

函数说明:获取数组长度。

以上就是Fastdb的详细介绍和所有接口的解释,希望对大家有用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/444441.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Groovy语言

1 Groovy介绍 1.1 Groovy介绍 Groovy是一种编程语言&#xff0c;它结合了Java的强大功能和脚本语言的简洁性。它具有动态类型、易读的语法、与Java的紧密集成、脚本编程能力、强大的闭包等特点。 1.2 Groovy SQL介绍 Groovy SQL是 Groovy 编程语言的一部分&#xff0c;用于…

你应该打好你的日志,起码避免被甩锅

大家好&#xff0c;我是蓝胖子,相信大家或多或少都有这样的经历&#xff0c;当你负责的功能出现线上问题时&#xff0c;领导第一时间便是找到你询问原因&#xff0c;然而有时问题的根因或许不在你这儿&#xff0c;只是这个功能或许依赖了第三方或者内部其他部门&#xff0c;这个…

Spring Boot 自动装配的原理!!!

SpringBootApplication SpringBootConfiguration&#xff1a;标识启动类是一个IOC容器的配置类 EnableAutoConfiguration&#xff1a; AutoConfigurationPackage&#xff1a;扫描启动类所在包及子包中所有的组件&#xff0c;生…

Mint_21.3 drawing-area和goocanvas的FB笔记(七)

FreeBASIC gfx 基本 graphics 绘图 8、ScreenControl与屏幕窗口位置设置 FreeBASIC通过自建屏幕窗口摆脱了原来的屏幕模式限制&#xff0c;既然是窗口&#xff0c;在屏幕坐标中就有它的位置。ScreenControl GET_WINDOW_POS x, y 获取窗口左上角的x, y位置&#xff1b;ScreenC…

小程序网页view多行文本超出隐藏或显示省略号

实现效果&#xff1a; 限制两行&#xff0c;超出即显示省略号 实现&#xff1a;话不多说&#xff0c;展示代码 关键代码 .box{ width:100rpx; overflow:hidden; text-overflow: ellipsis;//超出省略号 display:-webkit-box; -webkit-line-clamp: 2;//显…

【数学】【组合数学】1830. 使字符串有序的最少操作次数

作者推荐 视频算法专题 本博文涉及知识点 数学 组合数学 LeetCode1830. 使字符串有序的最少操作次数 给你一个字符串 s &#xff08;下标从 0 开始&#xff09;。你需要对 s 执行以下操作直到它变为一个有序字符串&#xff1a; 找到 最大下标 i &#xff0c;使得 1 < i…

Android UI自动化测试框架—SoloPi简介

1、UI自动化测试简介 软件测试简介 ​软件测试是伴随着软件开发一同诞生的&#xff0c;随着软件规模大型化&#xff0c;结构复杂化&#xff0c;软件测试也从最初的简单“调试”&#xff0c;发展到当今的自动化测试。 ​ 自动化测试是什么呢&#xff1f;自动化测试是把以人为…

用C语言执行SQLite3的gcc编译细节

错误信息&#xff1a; /tmp/cc3joSwp.o: In function main: execSqlite.c:(.text0x100): undefined reference to sqlite3_open execSqlite.c:(.text0x16c): undefined reference to sqlite3_exec execSqlite.c:(.text0x174): undefined reference to sqlite3_close execSqlit…

从零开始:神经网络(1)——神经元和梯度下降

声明&#xff1a;本文章是根据网上资料&#xff0c;加上自己整理和理解而成&#xff0c;仅为记录自己学习的点点滴滴。可能有错误&#xff0c;欢迎大家指正。 一. 神经网络 1. 神经网络的发展 先了解一下神经网络发展的历程。从单层神经网络&#xff08;感知器&#xff09;开…

349. 两个数组的交集

349. 两个数组的交集 力扣题目链接(opens new window) 题意&#xff1a;给定两个数组&#xff0c;编写一个函数来计算它们的交集。 说明&#xff1a; 输出结果中的每个元素一定是唯一的。 我们可以不考虑输出结果的顺序。 对于看某个元素是否出现在一个集合中的 &#xff0c;…

STM32利用标准库的方式输出PWM(proteus仿真)

首先打开proteus仿真软件&#xff0c;绘制电路图&#xff1a; 其中示波器的添加很简单的&#xff0c;看图&#xff1a; 再来看看咱们最后程序的效果&#xff1a; 下面就是程序代码了&#xff0c;新建两个文件PWM.c和PWM.h文件&#xff0c;所属关系如图&#xff1a; 整个的编程思…

Redis 内存的优化

目录 前言 Redis 的内存碎片问题 判断Redis 内存碎片 如何清理内存碎片&#xff1f; 前言 我想讲一下怎么提高Redis 内存的利用率&#xff0c;redis 的数据是保存在内存中。对内存的利用率低&#xff0c;意味着存的数据很少&#xff0c;并不意味着就没有内存了&#xff0c…

如何在Mapbox GL中处理大的GEOJSON文件

Mapbox GL可以将 GeoJSON 数据由客户端(Web 浏览器或移动设备)即时转换为 Mapbox 矢量切片进行显示和处理。本文的目的是教大家如何有效加载和渲染大型 GeoJSON 源,并优化渲染显示速度,增强用户体验,减少客户端卡顿问题。本文以Mapbox 为例,至于其它框架原理大致相同,可…

中国联通云联网在多元行业应用中的核心地位与价值体现

在全球化浪潮与数字化转型的时代背景下&#xff0c;中国联通积极响应市场需求&#xff0c;推出以云联网为核心的全球化智能组网解决方案&#xff0c;突破地理限制&#xff0c;为各行业提供高效、安全、灵活的网络服务。该方案不仅涵盖传统的通信连接&#xff0c;更是深入到能源…

复盘-excel

excel-选列没有用&#xff0c;选小标题才可以 将簇状柱形图放置在一个新表上##### excel: 添加数据模型时&#xff0c;要通过套用表格格式与外部断开连接 透视分析2010年人数未解决(第四套&#xff09; 通过日期显示星期几 判断星期几 因为前面已经通过星期六&#xff0c…

【AcWing】蓝桥杯集训每日一题Day1|二分|差分|503.借教室(C++)

503. 借教室 503. 借教室 - AcWing题库难度&#xff1a;简单时/空限制&#xff1a;1s / 128MB总通过数&#xff1a;8052总尝试数&#xff1a;26311来源&#xff1a;NOIP2012提高组算法标签二分差分 题目内容 在大学期间&#xff0c;经常需要租借教室。 大到院系举办活动&…

Linux上安装torch-geometric(pyg)1.7.2踩坑记录

重点&#xff1a;1.一定要在创建虚拟环境的时候设置好python版本。2.一定要先确定使用1.X还是2.X的pyg库&#xff0c;二者不兼容。3.一定要将cuda、torch、pyg之间的版本对应好。所以&#xff0c;先确定pyg版本&#xff0c;再确定torch和cuda的版本。 结论&#xff1a;如果在u…

【解读】OWASP大语言模型应用程序十大风险

OWASP大型语言模型应用程序前十名项目旨在教育开发人员、设计师、架构师、经理和组织在部署和管理大型语言模型&#xff08;LLM&#xff09;时的潜在安全风险。该项目提供了LLM应用程序中常见的十大最关键漏洞的列表&#xff0c;强调了它们的潜在影响、易利用性和在现实应用程序…

基本计算器II

文章目录 题目解析算法解析算法模拟第一步 第二步第三步第四步第五步第六步最后一步 代码 题目解析 题目链接 我们先来看一下题目这个题目的意思很明确就是给你一个算数式让你计算结果并返回并且给了很多辅助条件来帮助你。 算法解析 那么我们来看看这个题目有哪些做法&…

Qt 定时器事件

文章目录 1 定时器事件1.1 界面布局1.2 关联信号槽1.3 重写timerEvent1.4 实现槽函数 启动定时器 2 定时器类 项目完整的源代码 QT中使用定时器&#xff0c;有两种方式&#xff1a; 定时器类&#xff1a;QTimer定时器事件&#xff1a;QEvent::Timer&#xff0c;对应的子类是QTi…