当前位置: 首页 > article >正文

Qt 元对象系统探秘:从 Q_OBJECT 到反射编程的魔法之旅

背景说明:Qt 背后的「魔法引擎」

如果你曾用 Qt 写过信号槽,或是在设计器里拖过控件改属性,一定对这个框架的“动态性”印象深刻:

  • 无需手动调用,信号能自动连接到槽函数;
  • 无需编译重启,界面上修改的属性值能实时生效;
  • 甚至能在运行时获取类的所有方法、属性,像查字典一样操作对象。

这一切神奇功能的背后,都依赖 Qt 独创的 元对象系统(Meta-Object System)。它是 Qt 的核心基础设施,支撑着信号槽、动态属性、反射机制等关键特性。本篇就一起来揭开元对象系统的神秘面纱,从 Q_OBJECT 宏在编译期施展的奇幻魔法,到元对象编译器(moc)的工作原理,再到动态属性与反射编程的实战,一步步看懂 Qt 如何让 C++ 拥有 “自我认知” 的能力。

一、Q_OBJECT 宏:给类插上元数据的翅膀

1. 一个改变类命运的宏

在 Qt 中,只要在类定义里写下 Q_OBJECT,这个类就拥有了“元对象”的超能力。比如下面这个简单的自定义类:

#include <QObject>
class MyClass : public QObject {Q_OBJECT
public:MyClass(QObject *parent = nullptr) : QObject(parent) {}void myMethod(int value);
signals:void mySignal(QString text);
public slots:void mySlot();
};

加上 Q_OBJECT 后,这个类不再是普通的 C++ 类 —— 它告诉 Qt 的元对象编译器(moc):我需要生成元数据!
灵魂拷问:为什么普通 C++ 无法实现这种动态性?
C++ 是静态语言,类的信息在编译后就被固化了,运行时无法获取类名、方法列表等信息。而 Qt 要实现信号槽、动态属性等功能,必须让类在运行时能“自我介绍”,这就需要一套额外的元数据系统。

2. Q_OBJECT 宏展开后做了什么?

当 moc 处理包含 Q_OBJECT 的类时,会生成一系列隐藏代码,主要做了三件事:

(1)声明元对象所需的静态成员
// 生成的元对象结构体指针(静态成员)
static const QMetaObject staticMetaObject;
// 重写 QObject::metaObject() 函数
virtual const QMetaObject *metaObject() const;
// 重写 QObject::qt_metacall() 函数(处理信号槽、属性操作)
virtual int qt_metacall(QMetaObject::Call, int, void **);
// 生成信号的元数据数组(信号编号、参数等)
static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);

这些成员是元对象系统的基础设施,尤其是 staticMetaObject,它像一本记录类所有信息的字典。

(2)注册信号、槽和属性

moc 会扫描类中的 signals、public slots 和 Q_PROPERTY,将它们的名称、参数类型、编号等信息存入元对象的信号槽表和属性表。例如:

  • 信号 mySignal(QString) 会被分配一个唯一编号(如 0,1,2…);
  • 槽函数 mySlot() 编号为 1;
  • 属性会记录名称、类型、读写方法等。
(3)生成元对象结构体(QMetaObject)

QMetaObject 是元数据的载体,包含类名、父类元对象指针、方法列表、属性列表、信号列表等信息。moc 会为每个带 Q_OBJECT 的类生成一个专属的 QMetaObject 实例,比如 MyClass 的元对象可能长这样:

static const QMetaObject MyClass::staticMetaObject = {"MyClass",            // 类名&QObject::staticMetaObject,  // 父类元对象// 方法列表(包括信号、槽、普通方法){0, "mySignal(QString)", 0, 0, ...},// 属性列表{0, "myProperty", &getMyProperty, &setMyProperty, ...},// 其他元数据...
};

二、元对象编译器(moc):代码背后的“翻译官”

1. moc 如何工作

moc(Meta-Object Compiler)不是传统意义上的编译器,而是一个预处理工具,专门处理包含 Q_OBJECT 的头文件。它的工作流程分为三步:

(1)扫描代码,提取元数据

moc 会解析头文件,识别出:

  • 类名、父类(必须继承自 QObject);
  • signals 声明的信号(无需实现,moc 会生成空函数);
  • public slots 或 slots 声明的槽函数(可以是普通成员函数);
  • Q_PROPERTY 声明的属性(附带类型、读写方法);
  • Q_ENUMS、Q_FLAGS 声明的枚举类型(用于元数据扩展)。
(2)生成元对象代码

moc 会生成一个名为 moc_xxx.cpp 的源文件(xxx 是类名),包含:

  • 元对象结构体 staticMetaObject 的定义;
  • metaObject() 函数的实现(返回 &staticMetaObject);
  • qt_metacall() 函数的实现(处理信号槽调用、属性操作);
  • 信号的默认实现(空函数,因为信号只需声明无需实现)。

例如,前面的 MyClass 经 moc 处理后,会生成 moc_MyClass.cpp,其中包含信号 mySignal 的空函数:

void MyClass::mySignal(QString text) {QMetaObject::activate(this, &staticMetaObject, 0, &text);
}

QMetaObject::activate 会触发所有连接到该信号的槽函数,这就是信号能自动分发的底层机制。

(3)与编译器协作

开发者需要在 CMakeLists.txt 或 .pro 文件中告诉构建系统:这个头文件需要 moc 处理!

# .pro 文件中
QT += core
HEADERS += myclass.h
moc_files += myclass.h  # 显式指定 moc 处理的文件

构建时,moc 会先处理头文件生成 moc_xxx.cpp,再将其与其他源码一起编译。

2. moc 的“特殊照顾”:为什么不能省略?

如果不使用 moc,仅靠 C++ 原生特性,无法实现以下功能:

  • 信号槽的动态连接:C++ 无法在运行时获取函数地址,而 moc 生成的元数据记录了信号和槽的编号与参数,让 QObject::connect 能通过字符串名称(如 “mySignal”)找到对应的函数。
  • 属性的反射访问:QObject::setProperty 和 property 函数依赖元对象的属性表,而属性表由 moc 生成。
  • 枚举类型的元数据支持:通过 Q_ENUMS 声明的枚举,moc 会将其转换为字符串列表,允许在运行时通过名称获取枚举值(如 QMetaEnum::fromName(“MyEnum”))。

三、动态属性:让对象拥有可读写的灵魂

1. 用 Q_PROPERTY 定义动态属性

在 Qt 中,通过 Q_PROPERTY 宏可以将类的成员变量或函数声明为动态属性,例如:

class MyWidget : public QWidget {Q_OBJECTQ_PROPERTY(QString userName READ userName WRITE setUserName)
public:QString userName() const { return m_userName; }void setUserName(const QString &name) { m_userName = name; }
private:QString m_userName;
};

Q_PROPERTY 告诉 moc:这个属性需要被元对象系统管理!。它有三个核心要素:

  • 名称(userName):属性的标识符;
  • READ 函数:获取属性值的函数(必须是常量成员函数);
  • WRITE 函数:设置属性值的函数(必须是成员函数)。

还可以选择性的声明 NOTIFY 信号(属性变化时触发)、RESET 函数(重置属性)等。

2. 运行时操作属性:无需知道类的定义

一旦属性被注册到元对象,就可以通过 QObject 的通用接口操作,甚至不需要知道类的具体定义:

MyWidget *widget = new MyWidget;
// 通过字符串名称设置属性(动态方式)
widget->setProperty("userName", "Alice");
// 通过字符串名称获取属性
QVariant value = widget->property("userName");  // value == "Alice"

这种动态读写能力在以下场景非常有用:

  • UI 设计器:Qt Designer 能读取 Q_PROPERTY 声明的属性,允许在界面上直接修改,无需编译代码;
  • 配置系统:将配置项定义为属性,通过读取配置文件动态设置对象状态;
  • 数据绑定:结合信号槽,实现属性变化时自动更新界面(如 QLineEdit 的 text 属性与标签同步)。

3. 进阶:属性的“魔法”扩展

(1)使用设计时属性(Design-Time Properties)

通过 Q_PROPERTY 的 DESIGNABLE、STORED 等关键字,控制属性在设计器中的可见性和存储行为:

Q_PROPERTY(bool debugMode READ debugMode WRITE setDebugMode DESIGNABLE false)

DESIGNABLE false 表示该属性不在设计器中显示,适合内部调试开关。

(2)属性的类型限制

Q_PROPERTY 支持基本类型(int、QString)、QObject 派生类、以及注册过的自定义类型(需用 Q_DECLARE_METATYPE,后文会讲)。例如:

Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
(3)属性变化信号(NOTIFY)

当属性值变化时,触发自定义信号,实现更灵活的联动:

class MyModel : public QObject {Q_OBJECTQ_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
public:int count() const { return m_count; }void setCount(int value) {if (m_count != value) {m_count = value;emit countChanged(value);  // 触发信号}}
signals:void countChanged(int value);
private:int m_count = 0;
};

当 count 属性变化时,countChanged 信号会被自动触发,可用于通知界面更新。

四、反射编程:让代码认识自己

1. 元对象:类的“自我描述手册”

通过 QObject::metaObject() 函数,每个对象都能获取自己的元对象,进而查询类的所有信息。例如:

MyClass obj;
const QMetaObject *meta = obj.metaObject();
qDebug() << "类名:" << meta->className();          // 输出 "MyClass"
qDebug() << "父类名:" << meta->superClass()->className();  // 输出 "QObject"

QMetaObject 提供了丰富的接口,可获取:

  • 方法列表(包括信号、槽、普通方法);
  • 属性列表;
  • 枚举类型列表;
  • 类的所有元数据。

2. QMetaMethod:运行时调用方法的钥匙

通过 QMetaMethod 类,可以在运行时调用对象的方法,无需提前知道方法名(动态调用)。例如,调用前面 MyClass 的 mySlot() 槽函数:

// 获取方法列表中的第一个槽函数(假设索引为 1)
QMetaMethod method = meta->method(1);
// 检查是否是槽函数
if (method.methodType() == QMetaMethod::Slot) {// 调用槽函数(无参数)method.invoke(&obj, Q_ARG(void, ));
}

更强大的是,还可以通过方法名动态查找并调用:

int methodIndex = meta->indexOfMethod("mySlot()");  // 获取方法编号
if (methodIndex != -1) {QMetaMethod(method).invoke(&obj);
}

注意:参数匹配问题
调用时需严格匹配方法的参数类型和个数,Qt 会通过 Q_ARG 宏转换参数类型。例如调用带 int 参数的方法:

method.invoke(&obj, Q_ARG(int, 42));

3. 自定义类型的元数据注册:Q_DECLARE_METATYPE

如果想在元对象系统中使用自定义类型(如 MyData 结构体),需要先注册,否则 Qt 无法识别:

struct MyData {int id;QString name;
};
// 声明元类型(头文件中)
Q_DECLARE_METATYPE(MyData)
// 在代码中注册(通常在 main 函数或初始化时)
qRegisterMetaType<MyData>("MyData");

注册后,自定义类型可以:

  • 作为信号槽的参数;
  • 作为 Q_PROPERTY 的类型;
  • 在反射编程中被正确处理。

4. 实战:用反射实现万能的对象编辑器

假设我们要实现一个通用工具,能显示任意 QObject 派生类的所有属性,并允许修改。通过元对象系统可以轻松实现:

void editObject(QObject *obj) {const QMetaObject *meta = obj->metaObject();// 遍历所有属性for (int i = 0; i < meta->propertyCount(); ++i) {QMetaProperty prop = meta->property(i);QString propName = prop.name();QVariant propValue = obj->property(propName);// 在界面上显示属性名和值,并提供编辑框// 当编辑框值变化时,调用 setProperty 设回对象connect(editField, &QLineEdit::textChanged, [obj, propName](const QString &text) {obj->setProperty(propName.toUtf8(), text);});}
}

这个工具无需为每个类编写专用代码,完全依赖元对象系统的反射能力,这就是通用编程的魅力。

五、元对象系统的暗线:信号槽的底层逻辑

1. 信号槽如何实现动态连接

当调用 QObject::connect(sender, SIGNAL(signalName(arg)), receiver, SLOT(slotName(arg))) 时,Qt 做了以下事情:
通过 sender 的元对象获取信号 signalName 的编号和参数类型;
通过 receiver 的元对象获取槽 slotName 的编号和参数类型;
检查参数兼容性(信号参数可隐式转换为槽参数);
在内部维护的连接列表中记录这条连接。
当信号被发射时(如调用 sender->signalName(…)),Qt 会遍历连接列表,找到对应的槽,通过 QMetaMethod::invoke 动态调用槽函数。

2. 为什么信号槽可以跨线程

Qt::QueuedConnection 是元对象系统的另一大亮点:当信号和槽位于不同线程时,Qt 会将调用封装成一个事件,放入 receiver 所在线程的事件队列,等待事件循环处理。这个过程依赖 qt_metacall 函数和线程间的事件传递,而元数据(方法编号、参数类型)是实现跨线程调用的关键。

六、元对象系统的适用边界与最佳实践

1. 哪些场景不需要 Q_OBJECT

虽然元对象系统很强大,但并非所有类都需要 Q_OBJECT:

  • 纯数据类(无信号槽、属性需求);
  • 不继承自 QObject 的类(元对象系统仅作用于 QObject 派生类);
  • 性能敏感的高频调用模块(moc 生成的代码会有少量开销)。

2. 避免元对象乱用,过于膨胀

过度使用 Q_PROPERTY 和复杂的信号槽会导致元对象变大,影响内存和性能。建议:

  • 仅将需要动态访问的成员声明为属性;
  • 合并相似的信号(如用 valueChanged(int) 替代多个具体值的信号);
  • 对自定义类型进行必要的精简(避免注册冗余的元数据)。

3. 调试技巧:打印元对象信息

通过 QMetaObject::toString() 可以打印类的元数据,方便调试:

qDebug() << obj.metaObject()->toString();

输出会包含类名、父类、方法列表、属性列表等,是排查信号槽连接错误的利器。

七、从元对象到未来:Qt 元编程的进阶方向

1. Qt 6 的新特性:无宏元对象(QML 兼容)

Qt 6 引入了 Q_OBJECT_NO_QT_MOC 模式,允许通过标准 C++ 特性(如 [[qt::metaobject]] attribute)定义元对象,减少对 moc 的依赖。这为未来与其他语言(如 Python、JavaScript)的深度集成铺平了道路。

2. 自定义元对象:扩展 Qt 的能力边界

通过继承 QMetaObject 并实现自定义的元数据逻辑,可以打造插件系统、脚本绑定等高级功能。例如,将 C++ 类暴露给 QML 时,本质上就是通过元对象系统实现语言间的桥梁。

最后总结:元对象系统,让代码拥有“自我意识”

Qt 的元对象系统是一场静悄悄的革命:它在 C++ 的静态世界里构建了一个动态的平行宇宙,让类能在运行时认识自己,让对象能超越编译期的限制自由交互。从 Q_OBJECT 宏的魔法,到 moc 生成的元数据,再到反射编程的无限可能,这套系统教会我们:
真正的编程智慧,在于找到约定与扩展的平衡点—— 用简洁的语法(宏)约定规则,用强大的工具(moc)生成基础设施,最终让开发者专注于业务逻辑,而非重复造轮子。
下次当你写下 Q_OBJECT 时,不妨想想背后的元对象系统:它不仅是几行代码,更是 Qt 框架设计最牛的缩影 ——让复杂的底层逻辑隐形,这,或许就是优秀框架的终极魅力。

相关文章:

Qt 元对象系统探秘:从 Q_OBJECT 到反射编程的魔法之旅

背景说明&#xff1a;Qt 背后的「魔法引擎」 如果你曾用 Qt 写过信号槽&#xff0c;或是在设计器里拖过控件改属性&#xff0c;一定对这个框架的“动态性”印象深刻&#xff1a; 无需手动调用&#xff0c;信号能自动连接到槽函数&#xff1b;无需编译重启&#xff0c;界面上修…...

sql 向Java的映射

优化建议&#xff0c;可以在SQL中控制它的类型 在 MyBatis 中&#xff0c;如果返回值类型设置为 java.util.Map&#xff0c;默认情况下可以返回 多行多列的数据...

Visual Studio未能加载相应的Package包弹窗报错

环境介绍&#xff1a; visulal studio 2019 问题描述&#xff1a; 起因&#xff1a;安装vs扩展插件后&#xff0c;重新打开Visual Studio&#xff0c;报了一些列如下的弹窗错误&#xff0c;即使选择不继续显示该错误&#xff0c;再次打开后任然报错&#xff1b; 解决思路&am…...

【HD-RK3576-PI】Docker搭建与使用

硬件&#xff1a;HD-RK3576-PI 软件&#xff1a;Linux6.1Ubuntu22.04 1.Docker 简介 Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言开发&#xff0c;遵循 Apache 2.0 协议。它可以让开发者将应用程序及其依赖项打包到一个轻量级、可移植的容器中&#xff0c;并在任…...

C语言实现用户管理系统

以下是一个简单的C语言用户管理系统示例&#xff0c;它实现了用户信息的添加、删除、修改和查询功能。代码中包含了详细的注释和解释&#xff0c;帮助你理解每个部分的作用。 #include <stdio.h> #include <stdlib.h> #include <string.h>#define MAX_USERS…...

【websocket】使用案例( ​JSR 356 标准)

目录 一、JSR 356方式&#xff1a;简单示例 1、引入依赖 2、注册端点扫描器 3、编写通过注解处理生命周期和消息 4、细节解读 5、总结 二、聊天室案例 方案流程 1、引入依赖 2、注册端点扫描器 3、编写一个配置类&#xff0c;读取httpsession 4、编写通过注解处理生…...

tcpdump`是一个非常强大的命令行工具,用于在网络上捕获并分析数据包

通过 tcpdump&#xff0c;你可以抓取网络流量&#xff0c;诊断网络问题&#xff0c;或分析通信协议的细节。下面是如何在 Linux 上使用 tcpdump 进行抓包的详细步骤。 1. 安装 tcpdump 在大多数 Linux 发行版中&#xff0c;tcpdump 是默认安装的。如果没有安装&#xff0c;可…...

IS-IS中特殊字段——OL过载

文章目录 OL 过载位 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Datacom专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2025年04月13日20点12分 OL 过载位 路由过载 使用 IS-IS 的过载标记来标识过载状态 对设备设置过载标记后&#xff…...

【时频谱分析】快速谱峭度

算法配置页面&#xff0c;也可以一键导出结果数据 报表自定义绘制 获取和下载【PHM学习软件PHM源码】的方式 获取方式&#xff1a;Docshttps://jcn362s9p4t8.feishu.cn/wiki/A0NXwPxY3ie1cGkOy08cru6vnvc...

Spring Boot 支持的内嵌服务器(Tomcat、Jetty、Undertow、Netty(用于 WebFlux 响应式应用))详解

Spring Boot 支持的内嵌服务器详解 1. 支持的内嵌服务器 Spring Boot 默认支持以下内嵌服务器&#xff1a; Tomcat&#xff08;默认&#xff09;JettyUndertowNetty&#xff08;用于 WebFlux 响应式应用&#xff09; 2. 各服务器使用示例 (1) Tomcat&#xff08;默认&#xf…...

微软Exchange管理中心全球范围宕机

微软已确认Exchange管理中心&#xff08;Exchange Admin Center&#xff0c;EAC&#xff09;发生全球性服务中断&#xff0c;导致管理员无法访问关键管理工具。该故障被标记为关键服务事件&#xff08;编号EX1051697&#xff09;&#xff0c;对依赖Exchange Online的企业造成广…...

基于AI的Web应用防火墙(AppWall)实战:漏洞拦截与威胁情报集成

摘要&#xff1a;针对Web应用面临的OWASP、CVE等漏洞攻击&#xff0c;本文结合群联AI云防护系统的AppWall模块&#xff0c;详解AI规则双引擎的防御原理&#xff0c;并提供漏洞拦截配置与威胁情报集成代码示例。 一、Web应用安全挑战与AppWall优势 传统WAF依赖规则库更新滞后&a…...

基于Qt的串口通信工具

程序介绍 该程序是一个基于Qt的串口通信工具&#xff0c;专用于ESP8266 WiFi模块的AT指令配置与调试。主要功能包括&#xff1a; 1. 核心功能 串口通信&#xff1a;支持串口开关、参数配置&#xff08;波特率、数据位、停止位、校验位&#xff09;及数据收发。 AT指令操作&a…...

CSS 字体学习笔记

在网页设计中&#xff0c;字体的使用对于提升用户体验和页面美观性至关重要。CSS 提供了一系列字体属性&#xff0c;用于控制文本的显示效果。以下是对 CSS 字体属性的详细学习笔记。 一、字体系列&#xff08;font-family&#xff09; 1. 字体系列的分类 在 CSS 中&#xf…...

《MySQL是怎样运行的》总结笔记

内容太多&#xff0c;主要总结一些自己认为重要的&#xff0c;另外太基础常见可能不会总结上。 字符集和比较规则 MySQL会通过把字符串编码后再进行比较大小并排序&#xff0c;有一些很早的字符集可能会不支持中文&#xff0c;比如ASCII、ISO 8859-1&#xff0c;现在最常用的…...

力扣每日打卡 1922. 统计好数字的数目 (中等)

力扣 1922. 统计好数字的数目 中等 前言一、题目内容二、解题方法1. 暴力解法&#xff08;会超时&#xff0c;此法不通&#xff09;2. 快速幂运算3. 组合计数的思维逻辑分析组合计数的推导例子分析思维小结论 4.官方题解4.1 方法一&#xff1a;快速幂 三、快速幂运算快速幂运算…...

上层 Makefile 控制下层 Makefile 的方法

在复杂的项目中&#xff0c;通常会将项目划分为多个模块或子项目&#xff0c;每个模块都有自己的 Makefile。上层 Makefile 的作用是协调和控制这些下层 Makefile 的构建过程。下面是几种常见的示例&#xff0c;实现上层 Makefile 对下层 Makefile 的控制。 直接调用&#xff1…...

html简易实现推箱子小游戏原理(易上手)

实现效果 使用方向键移动&#xff0c;将橙色箱子推到绿色目标区域&#xff08;黑色块为墙&#xff0c;白色块为可通过区域&#xff0c;蓝球为小人&#xff09; 实现过程 <!DOCTYPE html> <html> <head><title>推箱子小游戏</title><style&g…...

搭建一个Spring Boot聚合项目

1. 创建父项目 打开IntelliJ IDEA&#xff0c;选择 New Project。 在创建向导中选择 Maven&#xff0c;确保选中 Create from archetype&#xff0c;选择 org.apache.maven.archetypes:maven-archetype-quickstart。 填写项目信息&#xff1a; GroupId&#xff1a;com.exampl…...

字符串与栈和队列-算法小结

字符串 双指针 反转字符串(双指针) 力扣题目链接 void reverseString(vector<char>& s) {for (int i 0, j s.size() - 1; i < s.size()/2; i, j--) {swap(s[i],s[j]);} }反转字符串II 力扣题目链接 遍历字符串的过程中&#xff0c;只要让 i (2 * k)&#…...

类似东郊到家的上门按摩预约服务系统小程序APP源码全开源

&#x1f525; 为什么上门按摩正在席卷全国&#xff1f; 万亿蓝海市场爆发 2024年中国按摩市场规模突破8000亿&#xff0c;上门服务增速达65% 90后成消费主力&#xff0c;**72%**白领每月至少使用1次上门按摩&#xff08;数据来源&#xff1a;艾媒咨询&#xff09; 传统痛点…...

Python | 在Pandas中按照中值对箱形图排序

箱形图是可视化数据分布的强大工具&#xff0c;因为它们提供了对数据集内的散布、四分位数和离群值的洞察。然而&#xff0c;当处理多个组或类别时&#xff0c;通过特定的测量&#xff08;如中位数&#xff09;对箱形图进行排序可以提高清晰度并有助于揭示模式。在本文中&#…...

[实战] 二分查找与哈希表查找:原理、对比与C语言实现(附完整C代码)

二分查找与哈希表查找&#xff1a;原理、对比与C语言实现 一、引言 在计算机科学中&#xff0c;高效的数据查找是核心问题之一。本文深入解析两种经典查找算法&#xff1a;二分查找与哈希表查找&#xff0c;从算法原理、时间复杂度、适用场景到完整C语言实现&#xff0c;提供…...

游戏引擎学习第215天

总结并为今天做铺垫 今天的工作内容是解决调试系统中的一个小问题。昨天我们已经完成了大部分的调试系统工作&#xff0c;但还有一个小部分没有完全处理&#xff0c;那就是关于如何层次化组织数据的问题。我们遇到的一个问题是&#xff0c;演示代码中仍有一个尚未解决的部分&a…...

【Redis】redis事物与管道

Redis 事务&#xff08;Transaction&#xff09; 事务概念 事务&#xff1a;是一组操作的集合&#xff0c;是不可分割的工作单元。Redis 事务特点&#xff1a; 一个事务可以一次执行多个命令。所有命令都被顺序化&#xff0c;形成一个队列。所有命令在执行 EXEC 时一次性、顺…...

Django信号使用完全指南示例

推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 **引言:****先决条件:****目录:****1. 什么是Django信号?****2:设置你的Django项目****2.1. 安装Django**2.2. 创建一个Django项…...

DeepSeek BLEU和ROUGE(Recall)的计算

以下是 BLEU Score (Bilingual ​Evaluation ​Understudy)和 ROUGE Score(​Recall-Oriented ​Understudy for ​Gisting ​Evaluation) 的原生Python实现&#xff08;不依赖第三方库&#xff09;&#xff0c;通过分步计算逻辑和示例详细说明。 一、BLEU Score 实现 核心逻辑…...

vulkanscenegraph显示倾斜模型(5.9)-vsg中vulkan资源的编译

前言 上一章深入剖析了GPU资源内存及其管理&#xff0c;vsg中为了提高设备内存的利用率&#xff0c;同时减少内存(GPU)碎片&#xff0c;采用GPU资源内存池机制(vsg::MemoryBufferPools)管理逻辑缓存(VkBuffer)与物理内存(VkDeviceMemory)。本章将深入vsg中vulkan资源的编译(包含…...

今日行情明日机会——20250409

今日行情还需要考虑关税对抗~ 2025年4月8日涨停的主要行业方向分析 1. 军工&#xff08;19家涨停&#xff09; 细分领域&#xff1a;国防装备、航空航天、军民融合。催化因素&#xff1a;国家安全战略升级、国防预算增加、重大军工项目落地预期。 2. 免税&#xff08;15家涨…...

XHR、FetchAxios详解网络相关大片文件上传下载

以下是 XHR(XMLHttpRequest) 与 Fetch API 的全面对比分析,涵盖语法、功能、兼容性等核心差异: 一、语法与代码风格 XHR(基于事件驱动) 需要手动管理请求状态(如 onreadystatechange 事件)和错误处理,代码冗长且易出现回调地狱。 const xhr = new XMLHttpRequest(); x…...