目录
[1.1 核心概念](#1.1 核心概念)
[1.2 信号的本质](#1.2 信号的本质)
[1.3 槽的本质](#1.3 槽的本质)
二、信号与槽的基本使用
[2.1 connect函数连接信号与槽](#2.1 connect函数连接信号与槽)
[2.2 Qt Creator可视化生成信号槽](#2.2 Qt Creator可视化生成信号槽)
三、自定义信号与槽
[3.1 自定义信号与槽的语法规则](#3.1 自定义信号与槽的语法规则)
[3.2 基础自定义示例](#3.2 基础自定义示例)
[3.3 带参数的信号与槽(含重载)](#3.3 带参数的信号与槽(含重载))
四、信号与槽的连接方式
[4.1 一对一连接](#4.1 一对一连接)
[4.2 一对多连接](#4.2 一对多连接)
[4.3 多对一连接](#4.3 多对一连接)
五、信号与槽的高级用法
[5.1 信号与槽的断开(disconnect)](#5.1 信号与槽的断开(disconnect))
[5.2 Lambda表达式作为槽函数](#5.2 Lambda表达式作为槽函数)
六、信号与槽的优缺点
一、信号与槽概述
信号与槽是Qt特有的消息传输机制,用于解决控件间的通信问题。在传统GUI编程中,通常使用回调函数实现控件交互,但回调函数存在耦合度高、可读性差等问题。Qt的信号槽机制通过松耦合设计,让相互独立的控件能够灵活关联。
1.1 核心概念
- 事件:用户与控件的交互行为(如点击按钮、关闭窗口、键盘输入等)。
- 信号 :事件触发时,控件发出的"通知"(如按钮被点击时发出
clicked()信号)。 - 槽 :接收信号并做出响应的函数(如窗口接收
clicked()信号后执行close()函数关闭自身)。 - 关联 :通过
connect()函数将信号发送者、信号、接收者、槽函数绑定,实现"信号触发→槽函数执行"的逻辑。
核心价值:信号发送者无需知道接收者的存在,接收者也无需知道哪些信号触发自己,完全解耦。例如"按钮"和"窗口"是独立控件,通过信号槽关联后,点击按钮即可关闭窗口。
1.2 信号的本质
信号的本质是事件的抽象表示,当用户操作控件(或系统触发事件)时,Qt对应的类会自动发出对应的信号。
关键特性:
- 信号由实例化的类对象发出(如
QPushButton实例发出clicked()信号)。 - 信号的呈现形式是函数(称为信号函数),无需开发者实现,Qt通过元编程(Meta Programming)自动生成。
- 信号函数用
signals关键字修饰,只需要声明,不需要定义。 - 常见信号场景:
- 控件交互:按钮点击(
clicked())、输入框文本变化(textChanged())。 - 系统事件:窗口刷新(
paintEvent())、鼠标移动(mouseMoveEvent())、键盘按下(keyPressEvent())。
- 控件交互:按钮点击(
1.3 槽的本质
槽(Slot)是响应信号的普通C++函数 ,与常规函数的区别仅在于:槽函数可以通过connect()与信号关联,当信号触发时自动执行。
关键特性:
- 槽函数可以定义在
public、protected、private作用域,支持参数和重载,但不能有默认参数。 - 槽函数需要显式实现(与普通函数一致),用
public slots/protected slots/private slots修饰(Qt5后可直接放在public等作用域,无需slots关键字)。 - 槽函数可直接调用(不依赖信号触发)。
- 底层逻辑:信号槽机制本质是函数调用,例如"点击按钮关闭窗口"就是
clicked()信号函数调用close()槽函数。
二、信号与槽的基本使用
信号与槽的核心操作是"连接",Qt通过QObject类提供的静态函数connect()实现关联。QObject是Qt中绝大多数类的父类,所有支持信号槽的类都必须继承自QObject并添加Q_OBJECT宏。
2.1 connect函数连接信号与槽
函数原型(Qt5):
cpp
connect(
const QObject *sender, // 信号发送者(如按钮对象)
const char *signal, // 发送的信号(信号函数)
const QObject *receiver, // 信号接收者(如窗口对象)
const char *method, // 响应的槽函数
Qt::ConnectionType type = Qt::AutoConnection // 连接方式(默认自动)
);
现代Qt(Qt5+)推荐语法(类型安全):
cpp
connect(sender, &SenderClass::signalFunc, receiver, &ReceiverClass::slotFunc);
示例:点击按钮关闭窗口
cpp
#include "widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent) : QWidget(parent)
{
// 创建按钮(父对象为当前窗口)
QPushButton *btn = new QPushButton("关闭窗口", this);
// 调整窗口大小
resize(800, 600);
// 连接信号与槽:按钮点击信号 → 窗口关闭槽函数
connect(btn, &QPushButton::clicked, this, &QWidget::close);
}
Widget::~Widget() {}
注意:
- 发送者和接收者必须是
QObject子类实例(或间接继承)。- 信号函数和槽函数的参数列表需匹配(信号参数个数可≥槽函数,反之不行)。
- 若类中使用信号槽,必须在类声明中添加
Q_OBJECT宏。
2.2 Qt Creator可视化生成信号槽
Qt Creator提供UI设计器,可通过拖拽控件+可视化操作生成信号槽代码,无需手动编写connect()。
操作步骤:
- 新建Qt Widgets项目,勾选"生成UI设计文件"(.ui文件)。
- 双击
widget.ui进入UI设计界面,拖拽一个PushButton控件到窗口。 - 选中按钮 → 右键 → 转到槽(Go to Slot)→ 选择
clicked()信号 → 点击OK。 - Qt Creator自动生成槽函数声明(在
widget.h)和定义(在widget.cpp)。 - 在槽函数中添加业务逻辑(如关闭窗口)。



自动生成的代码:
-
widget.h(头文件声明):cpp#ifndef WIDGET_H #define WIDGET_H #include <QWidget> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); private slots: // 自动生成的槽函数(命名规则:on_对象名_信号名) void on_pushButton_clicked(); private: Ui::Widget *ui; }; #endif // WIDGET_H -
widget.cpp(源文件实现):cpp#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); } Widget::~Widget() { delete ui; } // 实现关闭窗口功能 void Widget::on_pushButton_clicked() { this->close(); // 窗口关闭 }
命名规则说明 :
自动生成的槽函数名格式为
on_XXX_SSS,其中:
XXX:控件的objectName属性(默认如pushButton)。SSS:信号名(如clicked)。
遵循该规则的槽函数会被Qt自动关联信号,无需手动connect(),但推荐显式使用connect()以提高可读性。
三、自定义信号与槽
Qt允许开发者自定义信号和槽,满足复杂业务场景(如自定义控件交互、跨类通信等)。自定义需遵循严格的语法规则。
3.1 自定义信号与槽的语法规则
1. 自定义信号规则
- 必须在
signals关键字下声明(无需public等修饰符)。 - 返回值必须为
void,只声明不实现(Qt自动生成实现)。 - 支持参数和重载。
- 发送信号时使用
emit关键字(可选,仅用于代码可读性)。
2. 自定义槽函数规则
- Qt5+:可声明在
public/protected/private作用域(无需slots关键字);Qt4及之前必须在public slots等下声明。 - 返回值必须为
void,需要显式实现。 - 支持参数和重载,参数列表需与关联的信号匹配(信号参数个数≥槽函数)。
3. 核心要求
- 信号和槽的类必须继承自
QObject并添加Q_OBJECT宏。 - 关联信号与槽时,需先完成
connect(),再发送信号(否则槽函数不响应)。
3.2 基础自定义示例
场景:老师发出"上课了"信号,学生响应"回到座位学习"
- 新建
Teacher类(信号发送者)和Student类(信号接收者),均继承自QObject。
步骤1:声明类(头文件)
-
teacher.h(老师类,发送信号):cpp#ifndef TEACHER_H #define TEACHER_H #include <QObject> class Teacher : public QObject { Q_OBJECT public: explicit Teacher(QObject *parent = nullptr); signals: // 自定义信号:上课了 void classBegin(); }; #endif // TEACHER_H -
student.h(学生类,接收信号):cpp#ifndef STUDENT_H #define STUDENT_H #include <QObject> #include <QDebug> class Student : public QObject { Q_OBJECT public: explicit Student(QObject *parent = nullptr); public slots: // 自定义槽函数:响应上课信号 void goToClass(); }; #endif // STUDENT_H -
widget.h(主窗口,实例化对象并关联):cpp#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include "teacher.h" #include "student.h" class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); private: Teacher *teacher; // 信号发送者 Student *student; // 信号接收者 }; #endif // WIDGET_H
步骤2:实现类(源文件)
-
teacher.cpp:cpp#include "teacher.h" Teacher::Teacher(QObject *parent) : QObject(parent) {} -
student.cpp:cpp#include "student.h" Student::Student(QObject *parent) : QObject(parent) {} // 槽函数实现:回到座位学习 void Student::goToClass() { qDebug() << "学生:回到座位,开始学习!"; } -
widget.cpp(关联信号与槽并发送信号):cpp#include "widget.h" #include <QPushButton> Widget::Widget(QWidget *parent) : QWidget(parent) { // 实例化对象 teacher = new Teacher(this); student = new Student(this); // 关联信号与槽:老师的classBegin信号 → 学生的goToClass槽函数 connect(teacher, &Teacher::classBegin, student, &Student::goToClass); // 创建按钮触发信号 QPushButton *btn = new QPushButton("上课铃响", this); btn->move(100, 100); resize(800, 600); // 按钮点击 → 发送老师的classBegin信号 connect(btn, &QPushButton::clicked, this, [=]() { emit teacher->classBegin(); // 发送自定义信号 }); } Widget::~Widget() {}
运行结果:

3.3 带参数的信号与槽(含重载)
信号和槽支持参数传递,核心规则:信号的参数列表必须与槽函数的参数列表兼容(信号参数个数≥槽函数,参数类型一致)。
场景:老师点名(传递学生姓名),学生应答
步骤1:重载信号与槽
-
teacher.h(重载带参数的信号):cppsignals: void callStudent(); // 无参信号 void callStudent(QString name); // 带参数信号(传递学生姓名) -
student.h(重载带参数的槽函数):cpppublic slots: void answer(); // 无参槽函数 void answer(QString name); // 带参数槽函数
步骤2:实现槽函数
-
student.cpp:cpp// 无参应答 void Student::answer() { qDebug() << "学生:到!"; } // 带参数应答(接收学生姓名) void Student::answer(QString name) { qDebug() << "学生" << name << ":到!"; }
步骤3:关联重载信号与槽(需用函数指针指定)
由于重载函数存在歧义,需通过函数指针明确指定关联的信号和槽:
cpp
#include "widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent) : QWidget(parent)
{
teacher = new Teacher(this);
student = new Student(this);
// 1. 关联无参信号与槽(用函数指针消除歧义)
void (Teacher::*signalNoParam)() = &Teacher::callStudent;
void (Student::*slotNoParam)() = &Student::answer;
connect(teacher, signalNoParam, student, slotNoParam);
// 2. 关联带参数信号与槽(用函数指针消除歧义)
void (Teacher::*signalWithParam)(QString) = &Teacher::callStudent;
void (Student::*slotWithParam)(QString) = &Student::answer;
connect(teacher, signalWithParam, student, slotWithParam);
// 按钮1:触发无参信号
QPushButton *btn1 = new QPushButton("点名(无参)", this);
btn1->move(100, 100);
// 按钮2:触发带参数信号
QPushButton *btn2 = new QPushButton("点名(带参数)", this);
btn2->move(100, 150);
resize(800, 600);
// 绑定按钮点击事件
connect(btn1, &QPushButton::clicked, this, [=]() {
emit teacher->callStudent(); // 发送无参信号
});
connect(btn2, &QPushButton::clicked, this, [=]() {
emit teacher->callStudent("小明"); // 发送带参数信号
});
}
运行结果:

参数匹配规则:
- 信号参数个数可以多于槽函数(多余参数会被忽略)。
- 槽函数参数个数不能多于信号(否则无法匹配,编译报错)。
- 参数类型必须一致(如信号是
int,槽函数不能是QString)。
四、信号与槽的连接方式
信号与槽支持多种连接模式,满足不同场景下的通信需求,核心分为一对一、一对多、多对一三种。
4.1 一对一连接
两种形式:
- 一个信号连接一个槽函数。
- 一个信号连接另一个信号(信号转发)。
形式1:信号→槽
cpp
// 按钮点击 → 窗口关闭(一对一)
connect(btn, &QPushButton::clicked, this, &QWidget::close);
形式2:信号→信号(转发)
cpp
// 按钮点击 → 转发为老师的classBegin信号
connect(btn, &QPushButton::clicked, teacher, &Teacher::classBegin);
// 老师的classBegin信号 → 学生的goToClass槽函数
connect(teacher, &Teacher::classBegin, student, &Student::goToClass);
场景:需要多层信号传递时(如子控件信号转发给父控件)。
4.2 一对多连接
一个信号触发多个槽函数,槽函数执行顺序与connect()调用顺序一致。
示例:点击按钮触发三个槽函数
cpp
#include "widget.h"
#include <QPushButton>
#include <QDebug>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr) : QWidget(parent)
{
QPushButton *btn = new QPushButton("触发多个槽", this);
btn->move(100, 100);
resize(800, 600);
// 一个信号连接三个槽函数
connect(btn, &QPushButton::clicked, this, &Widget::slot1);
connect(btn, &QPushButton::clicked, this, &Widget::slot2);
connect(btn, &QPushButton::clicked, this, &Widget::slot3);
}
private slots:
void slot1() { qDebug() << "执行槽函数1"; }
void slot2() { qDebug() << "执行槽函数2"; }
void slot3() { qDebug() << "执行槽函数3"; }
};
运行结果:

4.3 多对一连接
多个信号触发同一个槽函数,任何一个信号触发都会执行该槽。
示例:两个按钮+一个键盘信号,共同触发"打印信息"槽函数
cpp
#include "widget.h"
#include <QPushButton>
#include <QKeyEvent>
#include <QDebug>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr) : QWidget(parent)
{
// 按钮1
QPushButton *btn1 = new QPushButton("按钮1", this);
btn1->move(100, 100);
// 按钮2
QPushButton *btn2 = new QPushButton("按钮2", this);
btn2->move(100, 150);
resize(800, 600);
setFocusPolicy(Qt::StrongFocus); // 启用键盘焦点
// 三个信号连接同一个槽函数
connect(btn1, &QPushButton::clicked, this, &Widget::slotPrint);
connect(btn2, &QPushButton::clicked, this, &Widget::slotPrint);
// 键盘按下信号(需重写keyPressEvent)
}
protected:
// 重写键盘按下事件,发送自定义信号
void keyPressEvent(QKeyEvent *event) override
{
if (event->key() == Qt::Key_Space) {
emit spacePressed(); // 空格键按下信号
}
}
signals:
void spacePressed(); // 自定义键盘信号
private slots:
void slotPrint() { qDebug() << "触发共同槽函数!"; }
};
运行结果:
点击按钮1、按钮2或按下空格键,均输出:触发共同槽函数!

五、信号与槽的高级用法
5.1 信号与槽的断开(disconnect)
当不需要信号与槽的关联时,可使用disconnect()函数断开连接。用法与connect()完全一致。
示例:动态连接与断开
cpp
#include "widget.h"
#include <QPushButton>
#include <QDebug>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr) : QWidget(parent)
{
btn1 = new QPushButton("连接槽函数", this);
btn1->move(100, 100);
btn2 = new QPushButton("断开槽函数", this);
btn2->move(100, 150);
resize(800, 600);
// 按钮1:连接信号槽
connect(btn1, &QPushButton::clicked, this, [=]() {
connect(btn1, &QPushButton::clicked, this, &Widget::slotPrint);
qDebug() << "已连接槽函数";
});
// 按钮2:断开信号槽
connect(btn2, &QPushButton::clicked, this, [=]() {
disconnect(btn1, &QPushButton::clicked, this, &Widget::slotPrint);
qDebug() << "已断开槽函数";
});
}
private slots:
void slotPrint() { qDebug() << "槽函数执行"; }
private:
QPushButton *btn1;
QPushButton *btn2;
};

注意事项:
disconnect()的参数必须与connect()完全一致(包括发送者、信号、接收者、槽函数)。- 若发送者或接收者被销毁,Qt会自动断开关联,无需手动处理。
5.2 Lambda表达式作为槽函数
Qt5支持使用Lambda表达式作为槽函数,无需显式声明槽函数。
Lambda表达式语法:
cpp
[捕获列表](参数列表) 选项 -> 返回值类型 { 函数体 };
核心捕获列表说明:
| 捕获符号 | 含义 |
|---|---|
[] |
不捕获任何外部变量 |
[=] |
值传递捕获所有外部变量(副本,只读) |
[&] |
引用传递捕获所有外部变量 |
[this] |
捕获当前类的this指针(可访问成员) |
[a, &b] |
值传递捕获a,引用传递捕获b |
示例:Lambda作为槽函数
cpp
#include "widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent) : QWidget(parent)
{
QPushButton *btn = new QPushButton("关闭窗口", this);
resize(800, 600);
// Lambda表达式作为槽函数
connect(btn, &QPushButton::clicked, this, [=]() {
this->close(); // [=]捕获所有外部变量(值传递)
});
}
注意:
- Qt5+默认启用C++11,无需额外配置;Qt4需在
.pro文件添加CONFIG += C++11。- 避免使用
[&]捕获局部变量(局部变量销毁后,Lambda可能访问悬空引用)。
六、信号与槽的优缺点
优点:
- 松耦合:信号发送者和接收者无直接依赖,无需知道对方存在,便于维护和扩展。
- 灵活性高:支持一对一、一对多、多对一连接,支持参数传递和重载。
- 可读性强 :通过
connect()直接关联信号与槽,逻辑清晰,无需复杂回调链。
缺点:
- 效率略低:相比直接函数调用,信号槽需要遍历关联列表、参数编组等操作。
- 调试难度大:复杂连接关系下,信号传递路径难以追踪(可通过Qt Creator的"信号槽调试器"辅助)。
- 语法限制 :需继承
QObject并添加Q_OBJECT宏,不支持非QObject子类使用。