【深入C++多态:基于消息解析器的设计、实现与剖析】
深入C++多态:基于消息解析器的设计、实现与剖析
- 前言
- 多态代码示例
- 代码结构
- C++多态的核心知识点
- 多态的底层机制深入剖析
- 多态的设计模式
- 总结
前言
在C++面向对象编程中,多态(Polymorphism)是实现灵活性和扩展性的核心特性,允许不同类型的对象通过统一接口处理。本文通过一个消息解析器的实际案例,全面探索C++运行时多态的方方面面,包括虚函数、动态绑定、底层机制、设计模式以及潜在问题。我们将结合代码详细分析其设计意图、运行逻辑,并提供优化建议,帮助读者从初学者到资深开发者都能从中获益。
多态代码示例
假设我们正在开发一个通信系统,需要解析不同类型的消息帧(Message Frame)。每条消息包含一个16位消息ID(msgId)和字节数组形式的消息体(msgBody)。不同的消息ID对应不同的解析逻辑:
0x0100:设置参数消息,包含32位参数ID和参数值。
0x0101:简单消息,返回固定字符串。
0x0200:扩展消息,仅在特定解析器中支持。
为了支持灵活扩展,我们设计了一个基类 SimpleParser,负责通用消息解析,并通过派生类 ExtendedParser 添加对新消息类型的支持。代码使用C++11的智能指针和虚函数实现动态多态,展示了面向对象设计的精髓。
代码结构
代码结构
代码分为三个文件:
SimpleParser.hpp:定义消息帧结构、基类 SimpleParser 和派生类 ExtendedParser 的接口。
SimpleParser.cpp:实现基类和派生类的解析逻辑。
main.cpp:测试代码,验证多态行为。
让我们逐一分析代码,挖掘C++多态的知识点。
1. 数据结构
在 SimpleParser.hpp 中,定义了核心数据结构:
struct MessageFrame {uint16_t msgId;std::vector<uint8_t> msgBody;
};struct ParameterItem {uint32_t paramId;std::vector<uint8_t> paramValue;
};struct SetParameters {std::vector<ParameterItem> parameters;
};
MessageFrame:表示消息帧,msgId 标识消息类型,msgBody 存储字节数据。
ParameterItem:表示参数项,用于解析 0x0100 消息的参数ID和值。
SetParameters:聚合多个参数项,适用于设置参数场景。
这些结构使用 std::vector 管理动态数据,保证内存安全,为解析逻辑奠定了基础。
2. 基类:SimpleParser
SimpleParser 是解析器的基类,定义了核心接口:
class SimpleParser {
public:struct ParseResult {std::shared_ptr<void> payload;bool success;std::string errorMsg;};SimpleParser() = default;virtual ~SimpleParser() = default;virtual ParseResult parseMessageBody(const std::shared_ptr<MessageFrame>& frame);protected:virtual std::shared_ptr<SetParameters> parseSetParameters(const std::vector<uint8_t>& msgBody);std::string toHex(uint16_t value) {char buf[5];snprintf(buf, sizeof(buf), "%04X", value);return std::string(buf);}
};
关键点:
ParseResult:解析结果,包含成功标志、错误信息和通用负载(std::shared_ptr)。
虚函数:parseMessageBody 和 parseSetParameters 允许派生类重写。
虚析构函数:确保通过基类指针删除派生类对象时正确清理。
智能指针:参数使用 std::shared_ptr,增强内存安全。
parseMessageBody 的实现(SimpleParser.cpp)如下:
SimpleParser::ParseResult SimpleParser::parseMessageBody(const std::shared_ptr<MessageFrame>& frame) {ParseResult result;result.success = false;if (!frame) {result.errorMsg = "Invalid frame: frame is nullptr";return result;}uint16_t msgId = frame->msgId;const std::vector<uint8_t>& msgBody = frame->msgBody;switch (msgId) {case 0x0100:result.payload = parseSetParameters(msgBody);break;case 0x0101:result.payload = std::make_shared<std::string>("Simple Message");break;default:result.errorMsg = "Unsupported message ID in SimpleParser: 0x" + toHex(msgId);return result;}if (!result.payload) {result.errorMsg = "Failed to parse message body for message ID: 0x" + toHex(msgId);return result;}result.success = true;return result;
}
逻辑:根据 msgId 分派处理,支持 0x0100(调用 parseSetParameters)和 0x0101(返回字符串)。
错误处理:检查空指针和不支持的ID,提供详细错误信息。
动态分配:负载使用智能指针存储,确保资源管理安全。
parseSetParameters 解析 0x0100 消息:
std::shared_ptr<SetParameters> SimpleParser::parseSetParameters(const std::vector<uint8_t>& msgBody) {auto payload = std::make_shared<SetParameters>();if (msgBody.size() < 5) return nullptr;ParameterItem item;item.paramId = (msgBody[0] << 24) | (msgBody[1] << 16) | (msgBody[2] << 8) | msgBody[3];item.paramValue.assign(msgBody.begin() + 4, msgBody.end());payload->parameters.push_back(item);std::cout << "SimpleParser - Param ID: 0x" << std::hex << item.paramId<< ", Value: ";for (auto byte : item.paramValue) {std::cout << std::hex << (int)byte << " ";}std::cout << std::endl;return payload;
}
位运算:将4字节组合为32位参数ID(大端字节序)。
输出:打印参数ID和值,便于调试。
3. 派生类:ExtendedParser
ExtendedParser 继承 SimpleParser,扩展了对 0x0200 消息的支持,并重写了部分逻辑:
class ExtendedParser : public SimpleParser {
public:ExtendedParser() = default;~ExtendedParser() override = default;ParseResult parseMessageBody(const std::shared_ptr<MessageFrame>& frame) override;protected:std::shared_ptr<SetParameters> parseSetParameters(const std::vector<uint8_t>& msgBody) override;private:std::shared_ptr<std::string> parseExtendedMessage(const std::vector<uint8_t>& msgBody);
};
parseMessageBody 的实现:
SimpleParser::ParseResult ExtendedParser::parseMessageBody(const std::shared_ptr<MessageFrame>& frame) {ParseResult result;result.success = false;if (!frame) {result.errorMsg = "Invalid frame: frame is nullptr";return result;}result = SimpleParser::parseMessageBody(frame);if (result.success) {return result;}uint16_t msgId = frame->msgId;const std::vector<uint8_t>& msgBody = frame->msgBody;switch (msgId) {case 0x0200:result.payload = parseExtendedMessage(msgBody);break;default:result.errorMsg = "Unsupported message ID in ExtendedParser: 0x" + toHex(msgId);return result;}if (!result.payload) {result.errorMsg = "Failed to parse message body for message ID: 0x" + toHex(msgId);return result;}result.success = true;return result;
}
复用基类:优先调用 SimpleParser::parseMessageBody,处理 0x0100 和 0x0101。
扩展逻辑:处理 0x0200,调用 parseExtendedMessage 返回 “Extended Message”。
parseSetParameters 的重写:
std::shared_ptr<SetParameters> ExtendedParser::parseSetParameters(const std::vector<uint8_t>& msgBody) {auto payload = std::make_shared<SetParameters>();if (msgBody.size() < 5) return nullptr;ParameterItem item;item.paramId = (msgBody[0] << 24) | (msgBody[1] << 16) | (msgBody[2] << 8) | msgBody[3];item.paramValue.assign(msgBody.begin() + 4, msgBody.end());payload->parameters.push_back(item);std::cout << "ExtendedParser - Param ID: 0x" << std::hex << item.paramId<< ", Value: ";for (auto byte : item.paramValue) {std::cout << std::hex << (int)byte << " ";}std::cout << std::endl;if (item.paramId == 0x0000FFFF) {std::cout << "ExtendedParser - Detected Special Parameter: Value = " << (int)item.paramValue[0] << std::endl;}return payload;
}
扩展功能:检查参数ID是否为 0x0000FFFF,打印额外信息。
独立实现:不依赖基类逻辑,适合定制化需求。
4. 测试代码:main.cpp
main.cpp 创建三条测试消息,验证多态行为:
int main() {// 消息 0x0100:设置参数auto frame1 = std::make_shared<MessageFrame>();frame1->msgId = 0x0100;frame1->msgBody = {0x00, 0x00, 0xFF, 0xFF, 0xAA};// 消息 0x0101:简单消息auto frame2 = std::make_shared<MessageFrame>();frame2->msgId = 0x0101;frame2->msgBody = {};// 消息 0x0200:扩展消息auto frame3 = std::make_shared<MessageFrame>();frame3->msgId = 0x0200;frame3->msgBody = {};// 测试 SimpleParserstd::cout << "Testing SimpleParser:\n";std::shared_ptr<SimpleParser> simpleParser = std::make_shared<SimpleParser>();auto result1 = simpleParser->parseMessageBody(frame1);if (result1.success) {std::cout << "Parsed 0x0100 successfully\n";}auto result2 = simpleParser->parseMessageBody(frame2);if (result2.success) {std::cout << "Parsed 0x0101: " << *std::static_pointer_cast<std::string>(result2.payload) << "\n";}auto result3 = simpleParser->parseMessageBody(frame3);if (!result3.success) {std::cout << "Error: " << result3.errorMsg << "\n";}// 测试 ExtendedParserstd::cout << "\nTesting ExtendedParser:\n";std::shared_ptr<SimpleParser> extendedParser = std::make_shared<ExtendedParser>();result1 = extendedParser->parseMessageBody(frame1);if (result1.success) {std::cout << "Parsed 0x0100 successfully\n";}result2 = extendedParser->parseMessageBody(frame2);if (result2.success) {std::cout << "Parsed 0x0101: " << *std::static_pointer_cast<std::string>(result2.payload) << "\n";}result3 = extendedParser->parseMessageBody(frame3);if (result3.success) {std::cout << "Parsed 0x0200: " << *std::static_pointer_cast<std::string>(result3.payload) << "\n";}return 0;
}
多态体现:
simpleParser 调用基类实现。
extendedParser 根据消息ID动态调用基类或派生类的实现。
C++多态的核心知识点
多态分为编译时多态(函数重载、模板)和运行时多态(虚函数、继承)。本代码聚焦运行时多态,以下深入剖析其机制和应用。
1. 虚函数与动态绑定
定义
虚函数通过 virtual 关键字声明,允许派生类重写。调用虚函数时,C++运行时根据对象实际类型选择实现(动态绑定)。
代码中的体现
SimpleParser 定义了虚函数:
virtual ParseResult parseMessageBody(const std::shared_ptr<MessageFrame>& frame);
virtual std::shared_ptr<SetParameters> parseSetParameters(const std::vector<uint8_t>& msgBody);
ExtendedParser 使用 override 重写:
ParseResult parseMessageBody(const std::shared_ptr<MessageFrame>& frame) override;
std::shared_ptr<SetParameters> parseSetParameters(const std::vector<uint8_t>& msgBody) override;
动态绑定:
std::shared_ptr<SimpleParser> parser = std::make_shared<ExtendedParser>();
parser->parseMessageBody(frame); // 调用 ExtendedParser::parseMessageBody
override 关键字:确保函数签名匹配,增强代码可读性。
底层机制
动态绑定通过**虚函数表(vtable)和虚指针(vptr)**实现:
vtable:每个含虚函数的类生成一个静态数组,存储虚函数地址。例如:
vtable_SimpleParser = {&SimpleParser::parseMessageBody,&SimpleParser::parseSetParameters,&SimpleParser::~SimpleParser
};
vtable_ExtendedParser = {&ExtendedParser::parseMessageBody,&ExtendedParser::parseSetParameters,&ExtendedParser::~ExtendedParser
};
vptr:对象内存中包含一个指向 vtable 的指针,调用虚函数时通过 vptr 查找地址。
流程:
获取对象的 vptr。
从 vtable 中查找函数地址(固定索引)。
执行目标函数。
性能
开销:虚函数调用需一次内存访问(vptr 到 vtable),略慢于直接调用。
影响:在低频场景(如消息解析)可忽略,但在高频循环中需谨慎。
2. 虚析构函数
基类声明了虚析构函数:
virtual ~SimpleParser() = default;
必要性:通过基类指针删除派生类对象时,确保调用 ExtendedParser::~ExtendedParser
。
代码中的体现:
std::shared_ptr<SimpleParser> parser = std::make_shared<ExtendedParser>();
// parser 销毁时,调用 ExtendedParser 的析构函数
智能指针:std::shared_ptr 自动管理生命周期,虚析构函数增强了扩展性。
3. 基类与派生类的协作
ExtendedParser::parseMessageBody 复用了基类逻辑:
result = SimpleParser::parseMessageBody(frame);
if (result.success) {return result;
}
复用性:处理 0x0100 和 0x0101 时直接使用基类实现。
扩展性:仅在基类失败时处理 0x0200,符合开闭原则(对扩展开放,对修改封闭)。
parseSetParameters 则完全重写,添加了特定逻辑:
if (item.paramId == 0x0000FFFF) {std::cout << "ExtendedParser - Detected Special Parameter: Value = " << (int)item.paramValue[0] << std::endl;
}
4. 智能指针与多态
代码使用 std::shared_ptr:
std::shared_ptr<SimpleParser> simpleParser = std::make_shared<SimpleParser>();
std::shared_ptr<SimpleParser> extendedParser = std::make_shared<ExtendedParser>();
内存安全:自动管理对象生命周期,避免泄漏。
多态支持:基类指针可指向派生类对象,虚函数调用基于实际类型。
效率:std::make_shared 一次性分配对象和控制块,优于单独构造。
5. 类型转换
ParseResult::payload 使用 std::shared_ptr:
if (result2.success) {std::cout << *std::static_pointer_cast<std::string>(result2.payload) << "\n";
}
灵活性:支持多种负载类型(如 SetParameters、std::string)。
风险:需手动确保类型匹配,可能引发未定义行为。
多态的底层机制深入剖析
运行时多态依赖虚函数表和虚指针,以下进一步探讨其实现细节。
1. 虚函数表(vtable)
生成:编译器为每个含虚函数的类生成 vtable,存储在静态存储区。
内容:包括所有虚函数地址(含析构函数)。
派生类:复制基类的 vtable,更新重写的函数地址。
示例:
对于 ExtendedParser 对象:
ExtendedParser object:
+-----------------+
| vptr -> vtable_ExtendedParser |
| (other members) |
+-----------------+
vtable_ExtendedParser:
[0]: &ExtendedParser::parseMessageBody
[1]: &ExtendedParser::parseSetParameters
[2]: &ExtendedParser::~ExtendedParser
2. 动态绑定流程
假设:
std::shared_ptr<SimpleParser> parser = std::make_shared<ExtendedParser>();
parser->parseMessageBody(frame);
执行步骤:
获取 parser 指向对象的 vptr。
从 vtable_ExtendedParser[0] 提取 &ExtendedParser::parseMessageBody。
调用该函数。
3. 性能与内存
内存:每个对象增加一个 vptr(通常 8 字节),vtable 是类级别,占用静态存储。
性能:虚函数调用需额外寻址,但在现代 CPU 上开销通常微秒级。
优化:
使用 final 禁用重写:
class ExtendedParser final : public SimpleParser { ... };
内联小函数,减少调用开销。
多态的设计模式
代码隐含了多种设计模式,体现了多态的灵活性。
1. 模板方法模式
定义:基类定义算法框架,派生类实现具体步骤。
体现:
SimpleParser::parseMessageBody 是框架,分派 msgId。
parseSetParameters 是可重写的步骤。
优点:控制流程,允许定制化。
2. 策略模式
定义:通过接口切换实现。
体现:
SimpleParser 作为接口,SimpleParser 和 ExtendedParser 是策略。
客户端可动态选择:
std::shared_ptr<SimpleParser> parser = condition ? std::make_shared<ExtendedParser>() : std::make_shared<SimpleParser>();
- 工厂模式(潜在)
实现:
std::shared_ptr<SimpleParser> createParser(bool extended) {return extended ? std::make_shared<ExtendedParser>() : std::make_shared<SimpleParser>();
}
好处:封装对象创建,增强灵活性。
优缺点与改进建议
优点
扩展性:新增消息类型只需定义派生类。
复用性:ExtendedParser 复用基类逻辑。
安全性:智能指针和 std::vector 保证内存管理。
缺点
复杂性:虚函数增加调试难度。
性能:虚函数调用和动态分配有轻微开销。
类型安全:std::shared_ptr 需手动类型转换。
改进建议
类型安全:
使用 std::variant:
using Payload = std::variant<std::shared_ptr<SetParameters>, std::shared_ptr<std::string>>;
struct ParseResult {Payload payload;bool success;std::string errorMsg;
};
错误处理:
引入错误码:
enum class ParseError { InvalidFrame, UnsupportedMsgId, InvalidBody };
性能:
使用栈分配 MessageFrame。
避免不必要的 std::string 构造。
接口封装:
将 parseSetParameters 设为私有。
使用工厂函数创建解析器。
总结
通过消息解析器,我们深入探索了C++运行时多态的魅力:
虚函数与动态绑定:实现了解析逻辑的灵活分派。
基派生类协作:复用与扩展并存,体现了开闭原则。
智能指针:增强了内存安全,与多态无缝集成。
设计模式:模板方法、策略模式等提高了代码可维护性。
多态的底层机制(vtable、vptr)揭示了其高效性与复杂性并存的本质。理解这些细节,结合设计模式与优化技巧,能帮助开发者在复杂系统中优雅地应用多态。希望这篇文章为您提供了全面的视角,激发您在C++项目中更自信地使用多态!
相关文章:
【深入C++多态:基于消息解析器的设计、实现与剖析】
深入C多态:基于消息解析器的设计、实现与剖析 前言多态代码示例代码结构C多态的核心知识点多态的底层机制深入剖析多态的设计模式总结 前言 在C面向对象编程中,多态(Polymorphism)是实现灵活性和扩展性的核心特性,允许…...
Dockerfile 文件常见命令及其作用
Dockerfile 文件包含一系列命令语句,用于定义 Docker 镜像的内容、配置和构建过程。以下是一些常见的命令及其作用: FROM:指定基础镜像,后续的操作都将基于该镜像进行。例如,FROM python:3.9-slim-buster 表示使用 Pyt…...
Redis--持久化
一、持久化 Redis支持RDB和AOF两种持久化机制持久化功能有效地避免因进程退出造成数据丢失问题, 当下次重启时利用之前持久化的文件即可实现数据恢复。 二、RDB RDB 持久化是把当前进程数据⽣成快照保存到硬盘的过程,触发 RDB 持久化过程分为手动触发和…...
Markdown学习
Typora下载 Typora教程 标题 井号加空格——回车即可形成标题,几级标题几个井号。 字体 斜体——前后各一个*,回车 粗体——前后各两个*,回车 既斜体又粗体——前后各三个*,回车 删除线——前后各两个~(波浪号…...
Vulhub-DarkHole靶机通关攻略
下载链接:https://www.vulnhub.com/entry/darkhole-1,724/ 扫描ip arp-scan -l扫描端口 nmap 192.168.112.144 -p-扫描目录 dirsearch -u http://192.168.112.144/有一个登录页面,还有一个upload目录,但是还没有找到上传点 先注册一个用…...
UniRig ,清华联合 VAST 开源的通用自动骨骼绑定框架
UniRig是清华大学计算机系与VAST联合开发的前沿自动骨骼绑定框架,专为处理复杂且多样化的3D模型而设计。基于强大的自回归模型和骨骼点交叉注意力机制,UniRig能够生成高质量的骨骼结构和精确的蒙皮权重,大幅提升动画制作的效率和质量。 UniR…...
深入解析 sklearn 中的 LabelEncoder:功能、使用场景与注意事项
标题:深入解析 sklearn 中的 LabelEncoder:功能、使用场景与注意事项 摘要: LabelEncoder 是 sklearn 中用于类别标签编码的重要工具,能够将离散的类别型标签转换为模型可识别的数值格式。本文详细解析 LabelEncoder 的核心功能…...
红帽Linux网页访问问题
配置网络,手动配置 搭建yum仓库红帽Linux网页访问问题 下载httpd 网页访问问题:首先看httpd的状态---selinux的工作模式(强制)---上下文类型(semanage-fcontext)---selinux端口有没有放行semanage port ---防火墙有没有active---…...
Muduo库代码剖析 : EventLoop
本文初发于 “天目中云的小站”,同步转载于此 EventLoop 详解 EventLoop类似于Reactor模型中的反应堆(Reactor)和事件分发器(Demultiplex)的合并, 其目的在于高效的接收事件, 并正确分配给对应的事件处理器. EventLoop中有两类关键的子控件 : Channel 和 Poller. C…...
Python网络爬虫设计(一)
目录 一、网络爬虫 1、基本的爬虫 2、获取URL 3、查找网页源码关键字 4、代码实现 二、requests库 1、requests的优势和劣势 2、获取网页的其他库 (1)selenium库 (2)pyppeteer库 三、pyppeteer库 1、pyppeteer库的来历…...
GEO供应商盈达科技发布:AI信源占位白皮书
副标题:生成式AI时代的企业认知主权争夺战 发布日期:2025年4月15日 一、范式重构:从流量入口到认知主权的战略迁移 生成式AI的规则革命 73%的用户决策直接依赖AI生成内容,但68%的引…...
L1-4 拯救外星人
题目 你的外星人朋友不认得地球上的加减乘除符号,但是会算阶乘 —— 正整数 N 的阶乘记为 “N!”,是从 1 到 N 的连乘积。所以当他不知道“57”等于多少时,如果你告诉他等于“12!”,他就写出了“479001600”这个答案。 本题就请你…...
业务摆渡解锁信息孤岛,重塑数字医疗未来
某三甲医院的急诊科突然亮起红灯,一名车祸患者被紧急送入,主治医师需要调取其三个月前在科研专网存储的增强CT影像。若在两年前,这需要两位管理员手动导出、杀毒、跨网传输,耗时40分钟;而现在,系统自动触发…...
OpenCV中的轮廓近似方法详解
文章目录 引言一、什么是轮廓近似?二、OpenCV中的轮廓近似方法2.1Douglas-Peucker算法原理2.2函数原型 三、代码示例3.1. 基本使用 四、参数选择技巧五、与其他轮廓方法的比较六、总结 引言 在计算机视觉和图像处理中,轮廓是物体边界的重要表示形式。Op…...
4种方法将文件映射到内存提升读写速度
背景 考虑到以下应用需求,常将文件映射到内容,以提升读写效果。 高效文件读写:大文件操作时,避免多次read/write系统调用的开销。进程间通信(共享内存):多个进程映射同一文件,实现…...
367. 有效的完全平方数
给你一个正整数 num 。如果 num 是一个完全平方数,则返回 true ,否则返回 false 。 完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。 不能使用任何内置的库函数,如 sqrt 。 示例 1…...
Ubuntu2404装机指南
因为原来的2204升级到2404后直接嘎了,于是要重新装一下Ubuntu2404 Ubuntu系统下载 | Ubuntuhttps://cn.ubuntu.com/download我使用的是balenaEtcher将iso文件烧录进U盘后,使用u盘安装,默认选的英文版本, 安装后,安装…...
爬虫框架 - Coocan
安装 pip install coocan 演示...
S06-Kep的跨通道传输
每次分享一小点,进步都是实实在在。小编今天又来分享了!之前我们讲到的KepServer软件,是一个具备强大通讯能力的软件,但是当你的上位软件不够灵活的时候,又有多个通道的数据交互的需求,Kep的跨通道传输就为…...
Zookeeper单机三节点集群部署(docker-compose方式)
前提: 服务器需要有docker镜像zookeeper:3.9.3 或能连网拉取镜像 服务器上面新建文件夹: mkdir -p /data/zk-cluster/{data,zoo-cfg} 创建三个zookeeper配置文件zoo1.cfg、zoo2.cfg、zoo3.cfg,配置文件里面内容如下(三个文件内容一样): tickTime=2000 initLimit=10 …...
C++| 深入剖析std::list底层实现:链表结构与内存管理机制
引言 std::list的底层实现基于双向链表,其设计哲学与std::vector截然不同。本文将深入探讨其节点结构、内存分配策略及迭代器实现原理,揭示链表的性能优势和潜在代价。 1. 底层数据结构:双向链表 每个std::list节点包含: 数据域…...
mysql数据库的线程连接数、状态 、最大并发数、缓存等参数配置
mysql数据库的线程连接数、状态 、最大并发数、缓存等参数配置 https://www.modb.pro/db/1784385883449397248 mysql数据库的线程连接数、状态 、最大并发数、缓存等参数配置 SQL命令行临时设置操作 #查看mysql数据库的线程连接数: mysql> show global statu…...
HarmonyOS-ArkUI V2状态-PersistenceV2:持久化存储UI状态
PersistenceV2类是一个与AppStorageV2类用法非常相似的类。因为它俩是子类和父类的关系。如果不了解AppStorageV2,可以先跳转至了解一下这个类。 HarmonyOS-ArkUI V2工具类:AppStorageV2:应用全局UI状态存储-CSDN博客 PersistenceV2相比于其父类AppStorageV2而言,它存储的…...
App测试小工具
前言 最近app测试比较多,每次都得手动输入日志tag,手动安装,测完又去卸载,太麻烦。就搞了小工具使用。 效果预览 每次测试完成,点击退出本次测试,就直接卸载了,usb插下一个手机又可以继续测了…...
ZEP: 一种用于智能体记忆的时序知识图谱架构
摘要 我们介绍了Zep,一种新型的智能体记忆层服务,在深度记忆检索(DMR)基准测试中,超越了现有的最先进系统MemGPT。此外,Zep在比DMR更全面、更具挑战性的评估中表现优异,这些评估更好地反映了现实世界企业应用的需求。尽管现有的基于大语言模型(LLM)的检索增强生成(R…...
800 中值定理
文章目录 前言365366367368369370371372373总结 前言 中值定理貌似是压轴题,但是也没什么难的,我一定可以拿下。 background music : 《还是分开》张叶蕾 365 构造出罗尔定理需要的 F(x) 我现在没啥问题,然后就是要找出两个相等的点&…...
安装fvm可以让电脑同时管理多个版本的flutter、flutter常用命令、vscode连接模拟器
打开 PowerShellfvm安装 dart pub global activate fvm安装完成后,如果显示FVM无法识别,那么需要去添加环境变量path添加这个:C:\Users\Administrator\AppData\Local\Pub\Cache\bin 常用命令 fvm releases 查看用户可以装的flutter版本fvm l…...
多线程、JUC——面试问题自我总结
1、创建线程有几种方式 答:1、通过继承Thread类,但需要注意的是一个类继承Thread就不能继承其他的类了 2、实现Runnable接口 3、实现Callable接口,重写Call方法 4、线程池 2、线程状态是怎么转换 3、实现线程的方式Callable与Runnable区别 …...
LivePortrait 使用指南:让静态照片“动”起来的魔法工具
欢迎来到涛涛聊AI,先看效果 项目地址:https://github.com/KwaiVGI/LivePortrait 在人工智能技术飞速发展的今天,静态照片的“动态化”已成为数字创意领域的热门方向。LivePortrait 凭借其高效性、可控性和逼真效果,成为用户将照片转化为动态视频的首选方案。本文将从技术原…...
Aosp13 文件应用点击apk无反应的处理
最近遇到一个问题,在A13上,打开文件管理应用时,点击apk 无反应或者启动安装进程后安装完成或取消安装进程,再次点击apk 无反应。在此记录该问题。 做一下修改:root/package/ providers/DownloadProvider/下 jenkinsdel…...
用python比较两个mp4是否实质相同
下面这个脚本会使用 ffmpeg 和 ffprobe 检查两个视频文件在以下方面是否“实质相同”: ✅ 检查内容: 分辨率(宽高)帧率视频总帧数音频轨道数量和采样率视频时长视频帧哈希(可选) — 对比前 N 帧的图像感知…...
jmeter中文使用手册
1. 简介 Apache J JMeter 是 100%纯 java 桌面应用程序,被设计用来测试 C/S 结构的软件(例如 web 应用程序)。它可以被用来测试包括基于静态和动态资源程序的性能,例如静态文件,Java Servlets,Java 对象&a…...
【Linux】系统入门
【Linux】系统初识 起源开源 闭源版本内核内核编号 Linux的安装双系统(不推荐)WindowsLinuxvmware虚拟机vitualbox操作系统的镜像centos 7/ubuntu云服务器租用 Linux的操作lsmkdir 文件名pwdadduser userdel -rrm文件名cat /proc/cpuinfolinux支持编程vim code.c./a.out 运行程…...
DP34 【模板】前缀和 -- 前缀和
目录 一:题目 二:算法原理 三:代码实现 一:题目 题目链接:【模板】前缀和_牛客题霸_牛客网 二:算法原理 三:代码实现 #include <iostream> #include <vector> using namespac…...
2025年机动车授权签字人考试题库及答案
一、单选题 1、汽车一般由发动机、( )、车身、电气和电子设备四大部分组成。 A、底盘 B、变速箱 C、离合器 D、驾驶室 答案: A 2、轮式汽车的驱动形式常用符号"nm"表示,其中n代表车轮总数,m代表 ( )。 A、…...
【项目】构建高性能多线程内存池:简化版 tcmalloc 实现指南
00 引言 在高并发应用中,频繁的小块内存申请与释放不仅会带来性能瓶颈,还容易导致内存碎片问题。为此,内存池技术应运而生,而 tcmalloc(Thread-Caching Malloc)作为 Google 开源的高性能内存分配器&#x…...
C++23 Lambda 表达式上的属性:P2173R1 深度解析
文章目录 一、背景与动机(一)Lambda 表达式的发展历程(二)属性的重要性(三)P2173R1 提案的动机 二、语法与使用(一)属性的放置位置1. 普通 Lambda 表达式2. 泛型 Lambda 表达式3. 多…...
libaom 码率控制实验:从理论到实践的深度探索
libaom 码率控制模式介绍 在 libaom 中定义了四种码率控制模式,分别是 VBR、CBR、CQ、Q;枚举类型会被用在编码器配置结构体 aom_codec_enc_cfg 中,通过 rc_end_usage 字段来设置编码器的码率控制策略。具体应用范围如下: AOM_VBR…...
golang的slice扩容过程
Go 语言中的切片扩容机制是 Go 运行时的一个关键部分,它确保切片在动态增加元素时能够高效地管理内存。这个机制是在 Go 运行时内部实现的,涉及了内存分配、数据拷贝和容量调整。扩容的实现主要体现在 runtime.growslice 函数中。下面我们将深入分析 Go …...
MCP 集合网站
分享个超全 MCP 网站,以后找资源不用愁,不谢。 MCPServers | Model Context Protocol Implementation | MCPServers.cnMCPServers - Model Context Protocol Servers for AI model serving. The official platform for MCP, MCPServer, and Model Contex…...
C++: Initialization and References to const 初始化和常引用
cpp primer 5e, P97. 理解 这是一段很容易被忽略、 但是又非常重要的内容。 In 2.3.1 (p. 51) we noted that there are two exceptions to the rule that the type of a reference must match the type of the object to which it refers. The first exception is that we …...
ES通过API操作索引库
1. 导入restClient依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.12.1</version></dependency> 2. 了解ES核心客户端API 核心区别…...
MySQL:存储函数和存储过程
系列文章目录 1.MySQL编程基础 2.程序控制流语句 3.存储过程 4.游标 5.嵌入式SQL 文章目录 系列文章目录前言一、程序控制流语句:二、存储函数: 1.存储函数的特点:2.存储函数的定义:3.调用存储函数 三、存储过程:…...
visual studio安装字体
以下是在 Windows 系统中将 Visual Studio 字体更换为 JetBrains 字体(如 JetBrains Mono)的完整指南,涵盖从下载安装到高级优化的全流程: 一、下载并安装 JetBrains 字体 获取字体文件 访问 JetBrains Mono 官方下载页面&#x…...
网络安全·第四天·扫描工具Nmap的运用
今天我们要介绍网络安全中常用的一种扫描工具Nmap,它被设计用来快速扫描大型网络,主要功能包括主机探测、端口扫描以及版本检测,小编将在下文详细介绍Nmap相应的命令。 Nmap的下载安装地址为:Nmap: the Network Mapper - Free Se…...
SSM考研助手管理系统
🍅点赞收藏关注 → 添加文档最下方联系方式咨询本源代码、数据库🍅 本人在Java毕业设计领域有多年的经验,陆续会更新更多优质的Java实战项目希望你能有所收获,少走一些弯路。🍅关注我不迷路🍅 项目视频 03…...
通道注意力机制|Channel Attention Neural Network
一、通道注意力机制 论文:ECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks 近年来,通道注意力机制在提高深度卷积神经网络CNN的性能方面显示出了巨大潜力。然而,大多数现有方法致力于开发更复杂的注意力模块…...
844. 比较含退格的字符串
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。 注意:如果对空文本输入退格字符,文本继续为空。 示例 1: 输入:s "ab#c&quo…...
trl的安装与单GPU多GPU测试
文章目录 0 相关资料1 源码安装2 Qwen2.5-0.5B-Instruct 模型下载3 训练demo4 在多个 GPU/节点上进行训练总结 0 相关资料 https://github.com/huggingface/trl https://blog.csdn.net/weixin_42486623/article/details/134326187 TRL 是一个先进的库,专为训练后基…...
Java项目之基于ssm的学校小卖部收银系统(源码+文档)
项目简介 学校小卖部收银系统实现了以下功能: 学校小卖部收银系统的主要使用者分为: 管理员;管理员使用本系统涉到的功能主要有:主页,个人中心,用户管理,员工管理,商品分类管理&am…...