2.1 信号和槽
信号:singal
click(单击)
press(按压)
release(释放)
toggle(切换)
槽函数:slot
连接函数connect
参数1: 信号的发送者
参数2:发送的信号
参数3:信号的接受者
参数4:处理的槽函数
松散耦合
通过帮助文档寻找相应的信号和槽
eg:点击按钮,关闭窗口
//参数1 信号的发送者
//参数2 具体信号 发送的信号(函数的地址)
//参数3 信号的接受者
//参数4 处理的槽函数
下面两种实现方法相同
// connect(mybutton, &Mypushbutton::clicked, this, &myWidget::close);
connect(mybutton, &QPushButton::clicked, this, &QWidget::close);
connect(mybutton, SIGNAL(clicked(bool)), this, SLOT(close())); //这种方式也行 QT4 不做动态类型检查
connect 函数有多种实现形式 主要是运用的运算符重载
2.2 自定义的信号和槽
出现一个问题
undefined reference to `vtable for’…….”错误
重新构件项目
// ProManager 项目经理类
// Programmer 程序员
// 客户有需求, 项目经理出发信号:有需求, 程序员:完成需求
注意:
1.第一点:当信号与槽函数的参数数量相同时,它们参数类型要完全一致。
2.第二点:当信号的参数与槽函数的参数数量不同时,只能是信号的参数数量多于槽函数的参数数量,且前面相同数量的参数类型应一致,信号中多余的参数会被忽略。
此外,在不进行参数传递时,信号槽绑定时也是要求信号的参数数量大于等于槽函数的参数数量。
2.2.1自定义信号
1.返回值void 只需要声明,不需要实现
2.可以有参数,可以重载
#ifndef PROMANAGER_H
#define PROMANAGER_H
#include <QObject>
//项目经理
class ProManager : public QObject
{
Q_OBJECT
public:
explicit ProManager(QObject *parent = nullptr);
signals:
//自定义信号
//返回值void 只需要声明,不需要实现
//可以有参数,可以重载
void newDemand();
public slots:
};
#endif // PROMANAGER_H
2.2.2自定义槽
#ifndef PROGRAMMER_H
#define PROGRAMMER_H
#include <QObject>
class Programmer : public QObject
{
Q_OBJECT
public:
explicit Programmer(QObject *parent = nullptr);
signals:
public slots:
//早期QT版本必须写到这里,高版本可以写到public中,或者全局
//返回值是void,需要声明,也需要实现
//可以有参数,也可以重载
void completeDemand();
};
#endif // PROGRAMMER_H
// 槽函数
void Programmer::completeDemand()
{
qDebug() << "程序员完成需求";
}
2.2.3触发
先建立连接
然后通过函数的方式来触发
触发某个信号使用emit关键字,如下
#include "widget.h"
#include "ui_widget.h"
// ProManager 项目经理类
// Programmer 程序员
// 客户有需求, 项目经理出发信号:有需求, 程序员:完成需求
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个项目经理
this->manage = new ProManager(this);
//创建一个程序员
this->pro = new Programmer(this);
//创建链接 项目经理有需求
connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
//调用客户需求函数
clientNewDemand();
}
//信号触发
void Widget::clientNewDemand()
{
//客户需求 调用后 项目经理出发新需求的信号
emit manage->newDemand();
}
Widget::~Widget()
{
delete ui;
}
2.2.4 自定义信号和槽出现重载(QString 转为 cahr*)
QStirng-> char* 不会有引号
完成的需求 “增加功能”
这个是新增的槽函数
qDebug() << “完成的需求” << content.toUtf8().data();
需要利用函数指针,明确指向函数的地址
void (ProManager::* manageSingal)(QString) = &ProManager::newDemand;
QString 转为 cahr
ToUtf8() -> QByteArrary
data() 转为 cahr
#include "widget.h"
#include "ui_widget.h"
// ProManager 项目经理类
// Programmer 程序员
// 客户有需求, 项目经理出发信号:有需求, 程序员:完成需求
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个项目经理
this->manage = new ProManager(this);
//创建一个程序员
this->pro = new Programmer(this);
//创建链接 项目经理有需求
//connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
//调用客户需求函数
//clientNewDemand();
//链接带重载 如下写会发生二义性
//connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
//函数指针->函数地址
void (ProManager::* manageSingal)(QString) = &ProManager::newDemand;
void (Programmer::* programmerSlot)(QString) = &Programmer::completeDemand;
connect(manage, manageSingal, pro, programmerSlot);
//写成一体的
//connect(manage, static_cast<void (ProManager::*)(QString)>(&ProManager::newDemand),
pro, static_cast<void (Programmer::*)(QString)>(&Programmer::completeDemand));
connect(manage, SIGNAL(newDemand(QString)), pro, SLOT(completeDemand(QString))); //另一种方法,更直观,但是不检测
clientNewDemand();
}
//信号触发
void Widget::clientNewDemand()
{
//客户需求 调用后 项目经理出发新需求的信号
//emit manage->newDemand();
emit manage->newDemand("增加功能");
}
Widget::~Widget()
{
delete ui;
}
2.2.5 按钮触发
1.使用按钮连接函数
#include "widget.h"
#include "ui_widget.h"
// ProManager 项目经理类
// Programmer 程序员
// 客户有需求, 项目经理出发信号:有需求, 程序员:完成需求
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个项目经理
this->manage = new ProManager(this);
//创建一个程序员
this->pro = new Programmer(this);
//创建链接 项目经理有需求
//connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
//调用客户需求函数
//clientNewDemand();
//链接带重载 如下写会发生二义性
//connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
//函数指针->函数地址
void (ProManager::* manageSingal)(QString) = &ProManager::newDemand;
void (Programmer::* programmerSlot)(QString) = &Programmer::completeDemand;
connect(manage, manageSingal, pro, programmerSlot);
//connect(manage, SIGNAL(newDemand(QString)), pro, SLOT(completeDemand(QString)));
QPushButton *btn = new QPushButton("客户需求", this);
this->resize(400, 500);
connect(btn, &QPushButton::clicked, this, &Widget::clientNewDemand);
}
//信号触发
void Widget::clientNewDemand()
{
//客户需求 调用后 项目经理出发新需求的信号
//emit manage->newDemand();
emit manage->newDemand("增加功能");
}
Widget::~Widget()
{
delete ui;
}
2.信号连接信号
使用按钮信号,来触发需求信号
注意这里:为什么只能触发无参数的?
虽然前后都是信号,但是参数要匹配,如果前面的参数和后面的参数不匹配,无法进行传参操作。
#include "widget.h"
#include "ui_widget.h"
// ProManager 项目经理类
// Programmer 程序员
// 客户有需求, 项目经理出发信号:有需求, 程序员:完成需求
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个项目经理
this->manage = new ProManager(this);
//创建一个程序员
this->pro = new Programmer(this);
//创建链接 项目经理有需求
//connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
//调用客户需求函数
//clientNewDemand();
//链接带重载 如下写会发生二义性
//connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
//函数指针->函数地址
// void (ProManager::* manageSingal)(QString) = &ProManager::newDemand;
// void (Programmer::* programmerSlot)(QString) = &Programmer::completeDemand;
// connect(manage, manageSingal, pro, programmerSlot);
//connect(manage, SIGNAL(newDemand(QString)), pro, SLOT(completeDemand(QString)));
QPushButton *btn = new QPushButton("客户需求", this);
this->resize(400, 500);
void (ProManager::* manageSingal)(void) = &ProManager::newDemand;
void (Programmer::* programmerSlot)(void) = &Programmer::completeDemand;
connect(manage, manageSingal, pro, programmerSlot);
connect(btn, SIGNAL(clicked()), manage, SIGNAL(newDemand()));
disconnect(manage, manageSingal, pro, programmerSlot);
}
//信号触发
void Widget::clientNewDemand()
{
//客户需求 调用后 项目经理出发新需求的信号
//emit manage->newDemand();
emit manage->newDemand("增加功能");
}
Widget::~Widget()
{
delete ui;
}
3.断开信号
将两人的信号断开
然后再点击按钮也没有用了
disconnect(manage, manageSingal, pro, programmerSlot);
4.拓展+QT4版本
拓展
1、信号是可以链接信号
2、一个信号可以链接多个槽函数
3、多个信号 可以链接 同一个槽函数
4、信号和槽函数的参数,必须一一对应
QT4版本的连接信号和槽
connect(btn, SIGNAL(clicked()), manage, SIGNAL(newDemand()));
//不做检测
connect(btn, SIGNAL(clicked()), manage, SIGNAL(newDemand(QString)));
优点:参数直观,
缺点: 类型不做检测
QT5 以上支持QT4,反之不支持
为什么不做检测:
将内容转换为字符串进行比较
2.3 Lambda表达式
C++ 11新特性
早期配置:CONFIG += C++11
转载自:https://www.cnblogs.com/DswCnblog/p/5629165.html
1.Lambda表达式声明
[capture list] (params list) mutable exception-> return type { function body }
各项具体含义如下
- capture list:捕获外部变量列表
- params list:形参列表
- mutable指示符:用来说用是否可以修改捕获的变量
- exception:异常设定
- return type:返回类型
- function body:函数体
此外,我们还可以省略其中的某些成分来声明“不完整”的Lambda表达式,常见的有以下几种:
- 格式1声明了const类型的表达式,这种类型的表达式不能修改捕获列表中的值。
- 格式2省略了返回值类型,但编译器可以根据以下规则推断出Lambda表达式的返回类型:
(1):如果function body中存在return语句,则该Lambda表达式的返回类型由return语句的返回类型确定;
(2):如果function body中没有return语句,则返回值为void类型。
- 格式3中省略了参数列表,类似普通函数中的无参函数。
2.捕获外界变量
Lambda表达式可以使用其可见范围内的外部变量,但必须明确声明(明确声明哪些外部变量可以被该Lambda表达式使用)。那么,在哪里指定这些外部变量呢?Lambda表达式通过在最前面的方括号[]来明确指明其内部可以访问的外部变量,这一过程也称过Lambda表达式“捕获”了外部变量。 ```cppinclude
using namespace std;
int main() { int a = 123; auto f = [a] { cout << a << endl; }; f(); // 输出:123
//或通过“函数体”后面的‘()’传入参数
auto x = [](int a){cout << a << endl;}(123);
}
上面这个例子先声明了一个整型变量a,然后再创建Lambda表达式,该表达式“捕获”了a变量,这样在Lambda表达式函数体中就可以获得该变量的值。<br />类似参数传递方式(值传递、引入传递、指针传递),在Lambda表达式中,外部变量的捕获方式也有值捕获、引用捕获、隐式捕获。
值捕获:<br />值捕获和参数传递中的值传递类似,被捕获的变量的值在Lambda表达式创建时通过值拷贝的方式传入,因此随后对该变量的修改不会影响影响Lambda表达式中的值。
```cpp
int main()
{
int a = 123;
auto f = [a] { cout << a << endl; };
a = 321;
f(); // 输出:123
}
这里需要注意的是,如果以传值方式捕获外部变量,则在Lambda表达式函数体中不能修改该外部变量的值。
引用捕获
使用引用捕获一个外部变量,只需要在捕获列表变量前面加上一个引用说明符&。如下:
int main()
{
int a = 123;
auto f = [&a] { cout << a << endl; };
a = 321;
f(); // 输出:321
}
从示例中可以看出,引用捕获的变量使用的实际上就是该引用所绑定的对象。
隐式捕获
上面的值捕获和引用捕获都需要我们在捕获列表中显示列出Lambda表达式中使用的外部变量。除此之外,我们还可以让编译器根据函数体中的代码来推断需要捕获哪些变量,这种方式称之为隐式捕获。隐式捕获有两种方式,分别是[=]和[&]。[=]表示以值捕获的方式捕获外部变量,[&]表示以引用捕获的方式捕获外部变量。
int main()
{
int a = 123;
auto f = [=] { cout << a << endl; }; // 值捕获
f(); // 输出:123
}
int main()
{
int a = 123;
auto f = [&] { cout << a << endl; }; // 引用捕获
a = 321;
f(); // 输出:321
}
混合方式
上面的例子,要么是值捕获,要么是引用捕获,Lambda表达式还支持混合的方式捕获外部变量,这种方式主要是以上几种捕获方式的组合使用。
到这里,我们来总结一下:C++11中的Lambda表达式捕获外部变量主要有以下形式:
**
3.修改捕获变量
前面我们提到过,在Lambda表达式中,如果以传值方式捕获外部变量,则函数体中不能修改该外部变量,否则会引发编译错误。那么有没有办法可以修改值捕获的外部变量呢?这是就需要使用mutable关键字,该关键字用以说明表达式体内的代码可以修改值捕获的变量,示例:
int main()
{
int a = 123;
auto f = [a]()mutable { cout << ++a; }; // 不会报错
cout << a << endl; // 输出:123
f(); // 输出:124
}
4.表达式的参数
- 参数列表中不能有默认参数
- 不支持可变参数
- 所有参数必须有参数名
5.QT应用
connect(btn2, &QPushButton::clicked, this, [=](){
emit manage->newDemand("增加功能");
//在这里可以运行多个函数
});
如上,通过该表达式,可以实现参数传递(其实就是调用了一个函数)的功能,
而且在函数体里面可以调用多个函数
= 与 &区别
= 要可修改需要加mutable,而且是深拷贝一份结果,对原数据不产生影响。
int k = 5;
int m = [=]()mutable{
k = k + 10;
return k;
}();
qDebug() << "m=" << m;
qDebug() << "k=" << k;
运行结果:
&相当于引用,改变变量不需要加mutable,直接会改变,而且会对原数据产生影响。
int k = 5;
int m = [&](){
k = k + 10;
return k;
}();
qDebug() << "m=" << m;
qDebug() << "k=" << k;