Qt 高级开发 015:C++ 原生实现信号槽机制

Qt 高级开发 015:C++ 原生实现信号槽机制

  • [Bilibili 同步视频](#Bilibili 同步视频)
  • ✨前言
  • [🔍一、打破认知:信号槽从不是 Qt 专属](#🔍一、打破认知:信号槽从不是 Qt 专属)
  • [📚二、经典轻量信号槽库:Sigslot 设计溯源](#📚二、经典轻量信号槽库:Sigslot 设计溯源)
    • [2.1 库的诞生背景](#2.1 库的诞生背景)
    • [2.2 核心特性亮点 🚀](#2.2 核心特性亮点 🚀)
  • 💡三、生活化案例理解信号槽逻辑
  • [🛠️四、C++ 原生信号槽项目接入实战](#🛠️四、C++ 原生信号槽项目接入实战)
    • [4.1 源码文件引入](#4.1 源码文件引入)
    • [4.2 模拟窗口事件:手写信号槽演示代码](#4.2 模拟窗口事件:手写信号槽演示代码)
      • [1. 定义信号发送类](#1. 定义信号发送类)
      • [2. 定义槽函数处理类](#2. 定义槽函数处理类)
      • [3. 信号槽绑定 & 触发调用](#3. 信号槽绑定 & 触发调用)
    • [4.3 运行效果 & 核心原理](#4.3 运行效果 & 核心原理)
  • 📌五、工程价值与落地场景总结
  • [💫 写在最后](#💫 写在最后)

Bilibili 同步视频

Qt 高级开发 015:C++ 原生实现信号槽机制

✨前言

提起信号槽机制 ,绝大多数开发者第一时间都会联想到 Qt 框架。

很多人误以为信号槽是 Qt 独有的黑魔法、脱离 Qt 环境就无法使用。实则不然💡,信号槽只是一种设计思想与通信模式,完全可以用标准 C++ 原生实现,无需依赖 Qt 预编译、无需第三方重型框架。

WebRTC 开源项目中就深度落地了自研信号槽,而今天我们就深入拆解这套轻量、类型安全、线程安全的 C++ 信号槽实现原理、源码使用、手写实战,带你脱离 Qt 也能玩转事件订阅与解耦通信。


🔍一、打破认知:信号槽从不是 Qt 专属

我们先厘清三个核心疑问👇

  1. 只有 Qt 拥有信号槽机制吗?
    并不是。Qt 只是把信号槽封装得足够易用、普及度极高而已。

  2. 普通 C++ 项目、跨平台项目能用信号槽吗?
    完全可以。任何 C++ 项目都可以引入轻量信号槽库,实现模块间解耦、事件异步通知。

  3. 底层能否用纯 C++ 从零实现?
    可以。依托模板、继承、函数绑定特性,即可打造无依赖的原生信号槽。

🎯 行业实战佐证:

WebRTC 底层大量采用自研信号槽做信令通信、Socket 事件回调。

在 WebRTC 55824 版本代码中,异步 Socket 初始化信令阶段,大量绑定各类事件信号:

  • Socket 关闭 → 自动触发 onclose 回调

  • Socket 连接成功 → 触发 onconnect 回调

  • 远端消息到达 → 触发 onread 回调

这类事件分发逻辑,正是信号槽最典型的应用场景事件发起者无需知道接收者是谁,只需触发信号,槽函数自动响应,完美实现高内聚低耦合。

信号如 signal close eventsignal click event 定义在异步 Socket 底层类中,而 control sockethandling 等业务对象归属 peer client 类;peer client 再派生信号槽基类,从而整个链路实现事件联动🔗。


📚二、经典轻量信号槽库:Sigslot 设计溯源

2.1 库的诞生背景

这套极简 C++ 信号槽库,源自国外开发者 莎拉汤普森 的设计创作🌱。

开发者认为 Windows 原生 MFC 界面通信设计十分笨重:

  • 窗口间消息通信依赖 SendMessage 硬编码

  • 代码编写繁琐、层级混乱

  • 回调嵌套深、极难调试与后期维护

于是借鉴 Qt 信号槽设计思想,用标准 C++ 自研了 Sigslot 轻量信号槽库,摆脱 MFC 与 Qt 双重依赖。

2.2 核心特性亮点 🚀

  • 类型安全:编译期校验信号与槽参数匹配,杜绝运行时参数错误

  • 线程安全:天然支持多线程环境下信号分发,无需额外加锁封装

  • 纯 C++ 实现:无依赖、无预处理、不依赖 MOC 编译器

  • 代码极简:核心头文件 + 实现文件仅数百行,精简高效,极易嵌入任意项目

  • 解耦能力强:事件发送与业务处理完全隔离,符合开闭设计原则

2022 年相关技术论文正式收录该库,原文核心概述:

该论文介绍了 Sigslot 库,在标准 C++ 中实现了类型安全、线程安全的信号槽通信机制。


💡三、生活化案例理解信号槽逻辑

开关 & 灯具的经典模型,一秒读懂信号槽本质:

  1. 信号 :开关点击触发的 click 点击事件

  2. 槽函数:灯具的开灯、关灯业务逻辑

  3. 连接绑定:将开关信号与灯具响应函数关联

  4. 触发响应:点击开关(发射信号)→ 灯具自动执行开关逻辑(槽函数响应)

底层实现上:

  • 开关类继承信号基类,定义点击信号;

  • 灯具类继承槽基类,实现事件响应方法;

  • 通过连接函数建立信号与槽的绑定关系;

  • 后续只需触发信号,无需手动调用灯具方法,完全解耦。


🛠️四、C++ 原生信号槽项目接入实战

4.1 源码文件引入

Sigslot 核心仅有两个文件:

  • .h 头文件:模板类、信号定义、绑定接口声明

  • .cc 实现文件:核心逻辑实现

剔除注释与空行后,有效代码仅四五百行 ,体量极其轻量化📦。

使用方式极其简单:

  1. .h.cc 文件直接拷贝到任意 C++ 控制台 / 工程项目;

  2. 项目中引入头文件,并使用对应命名空间;

  3. 定义信号类、槽基类,完成绑定与事件触发。

4.2 模拟窗口事件:手写信号槽演示代码

我们模拟窗口左键点击、窗口绘制重绘两个典型事件,落地原生信号槽用法。

1. 定义信号发送类

cpp 复制代码
#include "sigslot.h"
// 窗口事件类:负责定义各类信号
class Window
{
public:
    // 无参信号:左键点击事件
    sigslot::signal<> LButtonClick;
    // 带参信号:窗口绘制事件,传递绘制参数
    sigslot::signal<int> PaintEvent;
};

💡关键说明:

  • 信号定义无需写括号,参数通过尖括号模板传递;

  • 支持无参、单参、多参自定义信号,编译期强类型校验。

2. 定义槽函数处理类

cpp 复制代码
#include <iostream>
// 槽处理类:继承sigslot槽基类
class WindowHandler : public sigslot::has_slots<>
{
public:
    // 对应左键点击槽函数
    void OnLButtonClick()
    {
        std::cout << "🔘 窗口左键被点击,触发槽函数响应" << std::endl;
    }

    // 对应绘制事件槽函数,接收信号传递的参数
    void OnPaint(int param)
    {
        std::cout << "🎨 窗口触发重绘事件,传入参数:" << param << std::endl;
    }
};

3. 信号槽绑定 & 触发调用

cpp 复制代码
int main()
{
    // 1. 创建信号对象、槽处理对象
    Window wnd;
    WindowHandler handler;

    // 2. 绑定信号与槽函数
    wnd.LButtonClick.Connect(&handler, &WindowHandler::OnLButtonClick);
    wnd.PaintEvent.Connect(&handler, &WindowHandler::OnPaint);

    // 3. 发射信号,自动触发对应槽函数
    wnd.LButtonClick();       // 触发无参信号
    wnd.PaintEvent(123);      // 触发带参信号,传递参数123

    return 0;
}

4.3 运行效果 & 核心原理

运行输出:

Plain 复制代码
🔘 窗口左键被点击,触发槽函数响应
🎨 窗口触发重绘事件,传入参数:123

🎯 核心逻辑拆解:

  1. 信号类只负责定义事件、发射事件,不关心谁来处理;

  2. 槽类只负责实现业务响应,不关心事件从哪发起;

  3. Connect 完成双向绑定,一次绑定、永久生效;

  4. 发射信号时加括号,即可自动遍历所有绑定槽函数并回调。


📌五、工程价值与落地场景总结

🌼 为什么推荐非 Qt 项目也自研 / 引入信号槽?

  1. 彻底解耦:模块间无硬依赖,事件发布者与接收者完全隔离;

  2. 极简易移植:纯 C++ 无依赖,可嵌入服务端、客户端、嵌入式 C++ 项目;

  3. 类型安全可靠:模板编译期校验,规避回调参数不匹配问题;

  4. 线程安全内置:适配多线程异步事件分发,无需自己封装队列;

  5. 行业通用标准:WebRTC 等顶级开源项目都在落地,工业级稳定性经过验证。

💫 写在最后

信号槽从来不是 Qt 的专属标签,它只是一种优雅的事件订阅 - 分发设计模式

掌握标准 C++ 原生信号槽实现,既能读懂 WebRTC 底层架构,也能在日常项目中替代臃肿的回调嵌套、原生消息机制,让代码结构更清晰、维护更简单、扩展更灵活

相关推荐
在放️10 小时前
Python 练习题讲解 2 · 循环计算
开发语言·python
我不是懒洋洋10 小时前
从零实现一个分布式链路追踪:TraceId与Span
c++
江华森11 小时前
高级 Bash 脚本编程指南 — 实战教程
开发语言·bash
森G11 小时前
78、框架分析------服务器源码解析----云视频服务项目
服务器·c++·qt
我不是懒洋洋11 小时前
【C++】string(string的成员变量、auto和范围for、string常用接口的说明、OJ题目、string的模拟实现)
c语言·开发语言·c++·visual studio
Brilliantwxx11 小时前
【C++】 C++11 知识点梳理(中)
开发语言·c++
j7~11 小时前
【C++】STL--Vector容器--拆析解剖Vector的实现以及Vector的底层详解(2)
开发语言·c++·动态二维数组·vector深度剖析·vector的实现·杨辉三角形
旖-旎12 小时前
《LeetCode 130 被围绕的区域 FloodFill DFS 解法》
c++·算法·深度优先·力扣·floodfill
三品吉他手会点灯18 小时前
C语言学习笔记 - 50.流程控制4 - 流程控制为什么非常非常重要
c语言·开发语言·笔记·学习
一只旭宝19 小时前
【C++入门精讲22】常见设计模式
c++·设计模式