Lambda简介:
c11新特性中加入了lambda表达式,所以Qt5.0 也支持,需在.pro文件中加入 CONFIG += c++11;
Lambda函数也就是一个函数,它的语法定义如下:
[capture](parameters) mutable ->return-type{statement}
1.[capture]:捕捉列表。捕捉列表总是出现在Lambda函数的开始处。实际上,[]是Lambda引出符。编译器根据该引出符判断接下来的代码是否是Lambda函数。捕捉列表能够捕捉上下文中的变量以供Lambda函数使用;
2.(parameters):参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略;
3.mutable:mutable修饰符。默认情况下,Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空);
4.->return-type:返回类型。用追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号”->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导;
5.{statement}:函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。
与普通函数最大的区别是,除了可以使用参数以外,Lambda函数还可以通过捕获列表访问一些上下文中的数据。具体地,捕捉列表描述了上下文中哪些数据可以被Lambda使用,以及使用方式(以值传递的方式或引用传递的方式)。语法上,在“[]”包括起来的是捕捉列表,捕捉列表由多个捕捉项组成,并以逗号分隔。捕捉列表有以下几种形式:
1.[var]表示值传递方式捕捉变量var;
2.[=]表示值传递方式捕捉所有父作用域的变量(包括this);
3.[&var]表示引用传递捕捉变量var;
4.[&]表示引用传递方式捕捉所有父作用域的变量(包括this);
5.[this]表示值传递方式捕捉当前的this指针。
上面提到了一个父作用域,也就是包含Lambda函数的语句块,说通俗点就是包含Lambda的“{}”代码块。上面的捕捉列表还可以进行组合,例如:
1.[=,&a,&b]表示以引用传递的方式捕捉变量a和b,以值传递方式捕捉其它所有变量;
2.[&,a,this]表示以值传递的方式捕捉变量a和this,引用传递方式捕捉其它所有变量。
不过值得注意的是,捕捉列表不允许变量重复传递。下面一些例子就是典型的重复,会导致编译时期的错误。例如:
3.[=,a]这里已经以值传递方式捕捉了所有变量,但是重复捕捉a了,会报错的;
4.[&,&this]这里&已经以引用传递方式捕捉了所有变量,再捕捉this也是一种重复。
typedef enum
{
add = 0,
sub,
mul,
divi
}type;
int main()
{
int a = 10;
int b = 20;
auto func = [=](type i)->int {
switch (i)
{
case add:
return a + b;
case sub:
return a - b;
case mul:
return a * b;
case divi:
return a / b;
}
};
cout<<func(add)<<endl;
}
Qt4的信号槽连接方式,以及Qt5新版的连接方式,简单演示一下lambda表达式的使用方式
代码如下:
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// 传统Qt是连接方式
// 传统Qt4连接方式为 信号发送者,信号,信号接受者,处理函数
QObject::connect(ui->pushButton,SIGNAL(clicked(bool)),this,SLOT(qT4_slot()));
//Qt5连接方式
//其实这么写的方式和Qt4没有啥却别,只是在Qt4 中引用了信号槽,在简单的使用时没有问题,但是在庞大的工程中,信号和曹 仅仅是宏替换,在编译的时候没有安全监测
//Qt5的新方法,在编译的时候就会有监测,如果我们手误操作失误,就会出现问题
QObject::connect(ui->pushButton_2,&QPushButton::clicked,this,&Widget::qT5_slot);
//Qt5 Lambda表达式
//这里需要注意 Lambda表达式是C++ 11 的内容,所以,需要再Pro项目文件中加入 CONFIG += C++ 11
QObject::connect(ui->pushButton_3,&QPushButton::clicked,[=](){qDebug()<<"lambda 表达式";});
}
Widget::~Widget()
{
delete ui;
}
void Widget::qT4_slot()
{
qDebug()<< "This is Qt 4 Connect method";
}
void Widget::qT5_slot()
{
qDebug()<< "This is Qt 5 Connect method";
}
在connect中使用lambda实现高效的信号/槽关联:
如果编译器不支持c++ 11可变参数模板,则信号或槽中的参数数量限制为6;如果多参数需要传递,可以考虑Lambda;
在Qt中,使用QCoreApplication::connect()
函数连接信号(signal)
与槽(slots)
的功能。
以下是connect
函数的三种重载形式:
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);
static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,const QObject *receiver, const QMetaMethod &method,Qt::ConnectionType type = Qt::AutoConnection);
inline QMetaObject::Connection connect(const QObject *sender, const char *signal,const char *member, Qt::ConnectionType type = Qt::AutoConnection) const;
可以在函数声明中明显看出各个形参对应的意义,分别是[信号发送者,信号,接收者,槽]
,比如需要实现一个widget中点击button关闭窗口的功能,在Qt4这等老版本中,connect
的写法是这样的:
connect(button,SIGNAL(QPushButton::cliked()),widget,SLOT(QWidget::close()));
每次都要添加SIGNAL/SLOT
修饰符就显得很麻烦,所以在Qt5中,改进了connect
的写法,可以直接用&代替修饰符
connect(button,&QPushButton::cliked,widget,&QWidget::close);
- 这样的写法更简洁但是有一个问题就是无法向
signal/slot
函数传参,只能对无参版本的信号/槽生效,所以在有参数的情况下,只能选用第一种写法。
在connect
函数中,还有一个重要的限制!在传参的情况下,slot函数的参数个数一定要小于等于signal函数的参数个数,这样当signal
函数是clicked()
这样的无参形式时,我们不能与带参数的slot
函数进行关联,这就意味着:如果我们想通过点击一个按钮实现一个较为复杂的功能,而这个功能的实现必须按需将实参传入slot函数,那么用传统的connect
方法时行不通的。
在Qt5的新版本中,connect
函数添加了对lambda的支持!——可以用lambda替代signal/slot
函数。
这一feature的引进大大简化了slot
函数的实现,更自由地扩充其功能,并且可以不受signal
参数的影响(因为lambda可以捕获参数)而且connect
的形式也进一步精简了。
比如我希望在点击一个button时,实现数据库的更新,页面的跳转,然后页面的关闭,那么只需要这么写:
connect(button,&QPushButton::cliked,[this,new_data,new_page]{
UpdateDatabase(new_data);
OpenNewPage(new_page);
close();
});
- 甚至连信号的接受者都不用指定了。