【C++ 类和数据抽象】消息处理示例(1):从设计模式到实战应用

目录

一、数据抽象概述

二、消息处理的核心概念

2.1 什么是消息处理?

2.2 消息处理的核心目标

三、基于设计模式的消息处理实现

3.1 观察者模式(Observer Pattern)

3.2 命令模式(Command Pattern)

四、实战场景:GUI 框架中的消息处理

4.1 模拟 Qt 信号槽机制

五、高级主题:多线程消息队列

5.1 基于队列的异步消息处理

六、消息处理的最佳实践

6.1 解耦优先

6.2 线程安全

6.3 错误处理

6.4 性能优化

七、总结


在 C++ 的复杂系统开发中,消息处理是实现组件解耦、异步通信和事件驱动的核心机制。无论是 GUI 框架中的按钮点击响应,还是分布式系统中的模块通信,消息处理都扮演着关键角色。本文将围绕设计模式(如观察者模式、命令模式)和实战场景(如 GUI 事件、多模块通信),深入探讨 C++ 中消息处理的实现方式。

一、数据抽象概述

数据抽象是一种编程技术,它允许我们定义数据类型,同时隐藏其内部实现细节。意味着,我们只需关注数据对象能做什么,而不是它们是如何做的。在C++中,数据抽象主要通过类和对象实现。类定义了数据成员和成员函数,其中数据成员用于存储数据,而成员函数用于操作这些数据。通过将数据成员设置为私有(private),确保了它们只能通过类的公共接口(public成员函数)访问。

数据抽象具有多个优点:

  • 安全性:数据抽象可以防止不恰当的数据访问,从而保护数据的安全性。
  • 简化复杂性:允许我们处理复杂系统时,只关注其高层次的抽象,而不是底层细节。
  • 易于维护和修改:由于实现细节被隐藏,修改内部实现时不会影响到使用这些数据的其他部分。
  • 可重用性:抽象数据类型可以被重复使用,从而提高代码的可重用性。

二、消息处理的核心概念

2.1 什么是消息处理?

消息是组件间传递的信息载体,包含事件类型、数据参数等内容。
消息处理指接收消息并执行相应逻辑的过程,通常涉及:

  • 消息发送者:产生消息的组件(如按钮点击、传感器数据更新)。
  • 消息接收者:处理消息的组件(如事件处理器、业务逻辑模块)。
  • 消息通道:连接发送者与接收者的通信链路(如函数调用、队列、信号槽)。

2.2 消息处理的核心目标

  • 解耦组件:发送者与接收者无需直接依赖,提高系统灵活性。
  • 异步通信:支持非阻塞消息传递,提升系统响应速度。
  • 可扩展性:方便添加新的消息类型或处理逻辑。

三、基于设计模式的消息处理实现

3.1 观察者模式(Observer Pattern)

①模式原理

  • 角色
    • 主题(Subject):维护观察者列表,发送消息。
    • 观察者(Observer):接收消息并执行处理逻辑。
  • 核心机制:主题与观察者通过抽象接口解耦,主题发送消息时自动通知所有注册的观察者。

②代码示例:按钮点击事件处理

#include <iostream>
#include <vector>
#include <memory>
#include <algorithm> // 使用 std::remove_if// 抽象观察者接口
class ButtonObserver {
public:// 纯虚函数,具体观察者需要实现该函数来处理按钮点击事件virtual void OnButtonClick() = 0;// 虚析构函数,确保在通过基类指针删除派生类对象时能正确调用派生类的析构函数virtual ~ButtonObserver() = default;
};// 具体观察者:日志记录器
class LogObserver : public ButtonObserver {
public:// 实现 OnButtonClick 函数,处理日志记录void OnButtonClick() override {std::cout << "Button clicked: Logging event..." << std::endl;}
};// 具体观察者:业务处理器
class BusinessObserver : public ButtonObserver {
public:// 实现 OnButtonClick 函数,处理业务逻辑void OnButtonClick() override {std::cout << "Button clicked: Processing business logic..." << std::endl;}
};// 主题:按钮类
class Button {
public:// 向观察者列表中添加观察者void AddObserver(std::shared_ptr<ButtonObserver> observer) {if (observer) {observers_.push_back(observer);} else {std::cerr << "Error: Attempt to add a null observer." << std::endl;}}// 从观察者列表中移除指定的观察者void RemoveObserver(std::shared_ptr<ButtonObserver> observer) {if (observer) {auto newEnd = std::remove_if(observers_.begin(), observers_.end(), [&observer](const std::shared_ptr<ButtonObserver>& obs) {return obs == observer;});if (newEnd != observers_.end()) {observers_.erase(newEnd, observers_.end());} else {std::cerr << "Error: Observer not found in the list." << std::endl;}} else {std::cerr << "Error: Attempt to remove a null observer." << std::endl;}}// 模拟按钮点击,发送消息给所有注册的观察者void Click() {std::cout << "Button clicked: Notifying observers..." << std::endl;for (const auto& observer : observers_) {if (observer) {observer->OnButtonClick();} else {std::cerr << "Error: Found a null observer in the list." << std::endl;}}}private:// 存储观察者的列表std::vector<std::shared_ptr<ButtonObserver>> observers_;
};// 主函数:测试消息处理流程
int main() {Button button;auto logObserver = std::make_shared<LogObserver>();auto businessObserver = std::make_shared<BusinessObserver>();button.AddObserver(logObserver);button.AddObserver(businessObserver);button.Click(); // 触发消息发送return 0;
}

3.2 命令模式(Command Pattern)

①模式原理

  • 角色
    • 命令(Command):封装消息内容和处理逻辑。
    • 调用者(Invoker):触发命令执行。
    • 接收者(Receiver):实际执行命令的组件。
  • 核心机制:将 “消息” 封装为命令对象,支持动态添加、撤销命令,实现异步消息处理。

②代码示例:文件操作命令队列

#include <iostream>
#include <vector>
#include <memory>// 抽象命令接口
class FileCommand {
public:virtual void Execute() = 0;virtual ~FileCommand() = default;
};// 具体命令:创建文件
class CreateFileCommand : public FileCommand {
public:CreateFileCommand(const std::string& filename) : filename_(filename) {}void Execute() override {std::cout << "Creating file: " << filename_ << std::endl;// 实际文件创建逻辑}private:std::string filename_;
};// 具体命令:删除文件
class DeleteFileCommand : public FileCommand {
public:DeleteFileCommand(const std::string& filename) : filename_(filename) {}void Execute() override {std::cout << "Deleting file: " << filename_ << std::endl;// 实际文件删除逻辑}private:std::string filename_;
};// 命令调用者:消息队列
class CommandQueue {
public:void AddCommand(std::shared_ptr<FileCommand> command) {commands_.push_back(command);}void ProcessCommands() { // 批量处理消息for (const auto& command : commands_) {command->Execute();}commands_.clear();}private:std::vector<std::shared_ptr<FileCommand>> commands_;
};// 主函数:测试命令模式
int main() {CommandQueue queue;queue.AddCommand(std::make_shared<CreateFileCommand>("data.txt"));queue.AddCommand(std::make_shared<DeleteFileCommand>("temp.txt"));std::cout << "Processing commands..." << std::endl;queue.ProcessCommands();return 0;
}

四、实战场景:GUI 框架中的消息处理

4.1 模拟 Qt 信号槽机制

Qt 的信号槽机制是 C++ 中消息处理的经典实现,通过元对象系统实现组件间解耦。以下是简化的模拟实现:

①信号槽核心类

#include <iostream>
#include <vector>
#include <functional>// 信号类:支持绑定槽函数
template <typename... Args>
class Signal {
public:void Connect(std::function<void(Args...)> slot) {slots_.push_back(slot);}void Emit(Args... args) { // 发送消息for (const auto& slot : slots_) {slot(args...);}}private:std::vector<std::function<void(Args...)>> slots_;
};// 按钮类:发送点击消息
class GuiButton {
public:Signal<> clicked; // 无参数信号
};// 主函数:模拟GUI消息处理
int main() {GuiButton button;// 绑定槽函数:日志输出button.clicked.Connect([]() {std::cout << "Button clicked (via signal-slot)!" << std::endl;});// 触发信号:模拟按钮点击std::cout << "Simulating button click..." << std::endl;button.clicked.Emit();return 0;
}

五、高级主题:多线程消息队列

5.1 基于队列的异步消息处理

在多线程场景中,消息队列是实现线程间通信的常用方式。以下是一个简单的线程安全消息队列实现:

①线程安全队列类

#include <iostream>
#include <queue>template <typename T>
class NonThreadSafeQueue {
public:void Enqueue(T message) {queue_.push(std::move(message));}T Dequeue() {T message = queue_.front();queue_.pop();return message;}bool Empty() {return queue_.empty();}private:std::queue<T> queue_;
};// 消息类型定义
struct Message {int type;std::string data;
};// 生产者线程(非线程安全,仅演示逻辑)
void Producer(NonThreadSafeQueue<Message>& queue) {Message msg = {1, "Hello, message queue!"};queue.Enqueue(msg);std::cout << "Producer sent message: " << msg.data << std::endl;
}// 消费者线程(非线程安全,仅演示逻辑)
void Consumer(NonThreadSafeQueue<Message>& queue) {while (!queue.Empty()) {Message msg = queue.Dequeue();std::cout << "Consumer received message: " << msg.data << std::endl;}
}int main() {NonThreadSafeQueue<Message> queue;Producer(queue);Consumer(queue);return 0;
}

六、消息处理的最佳实践

6.1 解耦优先

  • 使用抽象接口(如观察者模式中的ButtonObserver)隔离发送者与接收者。
  • 避免硬编码依赖,通过工厂模式或依赖注入创建消息处理器。

6.2 线程安全

  • 多线程环境中,对共享消息队列加锁(如std::mutex)或使用无锁队列。
  • 消息对象设计为不可变或线程安全,避免竞态条件。

6.3 错误处理

为消息处理函数添加异常捕获,避免未处理异常导致程序崩溃。 

void OnButtonClick() {try {// 消息处理逻辑} catch (const std::exception& e) {std::cerr << "Message handling error: " << e.what() << std::endl;}
}

6.4 性能优化

  • 批量处理消息(如命令模式中的ProcessCommands())减少函数调用开销。
  • 使用智能指针(如std::shared_ptr)管理消息对象,避免内存泄漏。

七、总结

模式 / 场景核心机制适用场景
观察者模式主题 - 观察者订阅关系GUI 事件、状态变更通知
命令模式消息封装为可执行对象撤销操作、异步任务队列
信号槽机制动态绑定消息处理器Qt 等 GUI 框架
多线程队列线程安全的消息传递通道跨线程通信、异步任务处理

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Android】自定义Trace

1&#xff0c;Trace分析 Android掉帧分析-CSDN博客 2&#xff0c;自定义Trace 以下&#xff0c;android.os.Trace公开了以下API 1&#xff0c;beginSection与endSection联合使用&#xff0c;只能在同一个线程 2&#xff0c;beginAsyncSection与endAsyncSection可以在不同线程…

基于tabula对pdf中的excel进行识别并转换成word(三)

上一节中是基于PaddleOCR对图片中的excel进行识别并转换成word优化&#xff0c;本节改变思路&#xff0c;直接从pdf中读取表格的信息&#xff0c;具体思路如下所述。 PDF中的表格数据如下截图所示&#xff1a; 一、基于tabula从PDF中提取表格 df_list tabula.read_pdf("…

Java中的接口和抽象类

Java 抽象类与接口&#xff1a;区别、应用与选择 在 Java 编程的世界里&#xff0c;抽象类和接口是两个极为重要的概念&#xff0c;它们在实现代码抽象、提高代码复用性和可维护性方面发挥着关键作用。然而&#xff0c;很多开发者在使用时容易混淆这两个概念。本文将深入探讨 …

Java读Excel:解析阿里云easyExcel导入文件的行号

文章目录 引言I 解析阿里云easyExcel导入文件的行号声明解析对象的基类判断Excel解析对象类型是否包含继承某个类 isAssignableFromJava 转换list类型并设置下标到元素对象属性II 封装excel 文件读取excel 文件读取用法文件导入上下文III 参数校验工具类校验参数是否合法 (jaka…

mmap核心原理和用途及其与内存映射段的关系

mmap 是 Linux/Unix 系统中的一个关键系统调用&#xff0c;全称是 Memory Map&#xff08;内存映射&#xff09;。它的核心功能是将 文件、设备或匿名内存 直接映射到进程的虚拟地址空间&#xff0c;从而实现高效的内存访问和操作。以下是其核心原理和用途的详细说明&#xff1…

数据库概论速成期中版

文章目录 引论数据库用户Casual usersNaive usersApplication programmersDatabase administrators 关系模型CAP数据库两种描述关系数据库的方式简单总结 第一范式规则第二范式规则举个例子符合第二规则的操作不符合第二规则的操作 第三范式规则key,superkey,null values,主键&…

解决调用Claude 3.7接口 403 Request not allowed问题

1. 遇到问题 Python 基于 Langchain 对接 Claude 3.7 大模型接口进行问答时&#xff0c;由于国内不在Claude支持的国家和地区&#xff0c;所以一直调不通&#xff0c;错误 anthropic.PermissionDeniedError: Error code: 403 - {error: {type: forbidden, message: Request…

Vue2+Vue3学习笔记

Vue基础介绍 下载并安装vue.js v2 https://v2.cn.vuejs.org/https://v2.cn.vuejs.org/ v3 https://v3.cn.vuejs.org/ 会重定向到Vue.js - 渐进式 JavaScript 框架 | Vue.jsVue.js - 渐进式的 JavaScript 框架https://cn.vuejs.org/ 从v2过渡到v3 在F盘创建v2v3学习笔记 并…

2025年KBS新算法 SCI1区TOP:长颖燕麦优化算法AOO,深度解析+性能实测

目录 1.摘要2.算法原理3.结果展示4.参考文献5.文章&代码获取 1.摘要 本文提出了一种新颖的元启发式算法——长颖燕麦优化算法&#xff08;AOO&#xff09;&#xff0c;该算法灵感来自动画燕麦在环境中的自然行为。AOO模拟了长颖燕麦的三种独特行为&#xff1a;(i) 通过自然…

CentosLinux系统crontab发现执行删除命令失效解决方法

权限或安全策略限制 ​​可能场景​​&#xff1a; ​​### ​​目录权限冲突​​&#xff1a; 你的目录权限为 drwxr-xr-x&#xff08;属主 mssql&#xff09;&#xff0c;但 cron 任务以 root 执行。 ​​风险点​​&#xff1a;若目录内文件属主为 mssql 且权限为 700&…

后验概率最大化(MAP)估计算法原理以及相具体的应用实例附C++代码示例

1. MAP估计基本原理 MAP&#xff08;Maximum A Posteriori&#xff0c;最大后验概率估计&#xff09;是贝叶斯推断中的重要概念&#xff0c;它的目标是&#xff1a; 给定观测数据&#xff0c;找到使得后验概率最大的参数值。 公式化表示&#xff1a; [ θ MAP arg ⁡ max ⁡…

16、路由守卫:设置魔法结界——React 19 React Router

一、魔法结界的本质 "路由守卫是霍格沃茨城堡的隐身斗篷&#xff0c;在时空裂隙中精准控制维度跃迁&#xff01;" 魔法部交通司官员挥舞魔杖&#xff0c;React Router 的嵌套路由在空中交织成星轨矩阵。 ——基于《国际魔法联合会》第7号时空协议&#xff0c;路由守…

从车道检测项目入门open cv

从车道检测项目入门open cv 前提声明&#xff1a;非常感谢b站up主 嘉然今天吃带变&#xff0c;感谢其视频的帮助。同时希望各位大佬积积极提出宝贵的意见。&#x1f60a;&#x1f60a;&#x1f60a;(❁◡❁)(●’◡’●)╰(▽)╯ github地址&#xff1a;https://github.com/liz…

【行业特化篇3】制造业简历优化指南:技术参数与标准化流程的关键词植入艺术

写在最前 作为一个中古程序猿,我有很多自己想做的事情,比如埋头苦干手搓一个低代码数据库设计平台(目前只针对写java的朋友),比如很喜欢帮身边的朋友看看简历,讲讲面试技巧,毕竟工作这么多年,也做到过高管,有很多面人经历,意见还算有用,大家基本都能拿到想要的offe…

如何在本地部署小智服务器:从源码到全模块运行的详细步骤

小智聊天机器人本地后台服务器源码全模块部署 作者&#xff1a;林甲酸 -不是小女子也不是女汉子 是大女子 更新日期&#xff1a;2025年4月29日 &#x1f3af; 前言&#xff1a;为什么要写这篇教程&#xff1f; 上周按照虾哥小智服务器的教程去部署本地后台&#xff0c;我用的是…

github开源项目添加开源协议,使用很简单

直接在 GitHub 网页上创建 进入你的 GitHub 仓库 打开你的项目仓库页面&#xff08;如 https://github.com/用户名/仓库名&#xff09;。 点击 "Add file" → "Create new file" 在仓库主页&#xff0c;点击右上角的 "Add file" 按钮&#xff…

8.idea创建maven项目(使用Log4j日志记录框架+Log4j 介绍)

8.idea创建maven项目(使用Log4j日志记录框架Log4j 介绍) 在 IntelliJ IDEA 的 Maven 项目中引入了 Log4j&#xff0c;并配置了日志同时输出到控制台和文件。 Log4j 提供了灵活的日志配置选项&#xff0c;可以根据项目需求调整日志级别、输出目标和格式。 1. 创建 Maven 项目 …

【和春笋一起学C++】函数——C++的编程模块

目录 1. 原型句法 2. 函数分类 3. 函数参数之按值传递 4. 数组作为函数参数 在C中&#xff0c;要使用函数&#xff0c;必须要有这三个方面&#xff1a; 函数原型&#xff0c;函数原型描述了函数到编译器的接口&#xff0c;函数原型一般放在include文件中。函数原型告诉编译…

深挖Java基础之:认识Java(创立空间/先导:Java认识)

今天我要介绍的是在Java中对Java的一些基本语法的认识与他们的运用&#xff0c;以及拟举例子说明和运用场景&#xff0c;优势和劣势&#xff0c; 注&#xff1a;本篇文章是对Java的一些基本的&#xff0c;简单的代码块的一些内容&#xff0c;后续会讲解在Java中的变量类型&…

Python+Selenium+Pytest+Allure PO模式UI自动化框架

一、框架结构 allure-report&#xff1a;测试报告base&#xff1a;定位元素封装data&#xff1a;数据log&#xff1a;日志文件page&#xff1a;页面封装文件夹report&#xff1a;缓存报告testcases&#xff1a;测试用例层utils&#xff1a;工具类run.py&#xff1a;执行文件 二…