Lambda

Lambda 表达式很有意思,相信很多人初次见到 Lambda 表达式都会不能理解有什么用,我也一样,看了视频教程之后,突然意识到,Lambda 真的是太好用了,它可以在某些情况下可以很大程度上简化代码。

应用场景

下面的代码给我的启发:实现通过信号槽的方式实现点击按钮时,触发修改按钮的名字为“停止”。connect 的最后一个参数其实是函数指针,当按钮触发了 clicked 信号时,将会调用该函数指针,那么借助 Lambda 就不需要重新另外定义一个成员函数来作为参数传入。

void Widget::testLambda()
{
QPushButton *pButton = new QPushButton(); pButton->setParent(this);
pButton->setText("启动"); connect(pButton, &QPushButton::clicked, this, [=](){
pButton->setText("停止");
}); }

可以自己写一个函数回调的方式来对比

#include <iostream>

void onEvent()
{
printf("onEvent\n");
} void notice(void (*pfunc)())
{
pfunc();
} int main(int argc, char *argv[])
{
notice(onEvent);
return 0;
}

使用 Lambda 的方式,可以看到代码简化了

#include <iostream>

void notice(void (*pfunc)())
{
pfunc();
} int main(int argc, char *argv[])
{
notice( [](){printf("Lambda2\n");} );
return 0;
}

介绍说明

从上面的例子可以看到,Lambda 其实本质就是一个函数,只不过这个函数没有名字,所以也叫匿名函数。适合在一些需要传递以函数为参数的表达式中,而函数本事的实现只有几句代码,可以不必另外声明定义函数,直接使用 Lambda 即可。

Lambda 原型说明:

[外部变量访问方式说明符] (参数表) 修饰符 ->返回值类型 {函数体} ()

Lambda 原型解析:

[外部变量访问方式说明符]

用于确定 Lambda 函数体可以用何种方式去访问外部成员

  • [] 中括号里面为空,即表示不访问任何外部成员变量。
  • [&] 以引用方式使用 Lambda 所在作用范围内所有可见的局部变量(包括Lambda所在类的 this)。
  • [=] 以值传递方式使用 Lambda 所在作用范围内所有可见的局部变量(包括Lambda所在类的 this)。
  • [=, &a] 除了a以引用传递方式使用,其他的所有变量以值传递方式使用。
  • [a] 只取a,并且以值传递方式使用。
  • [this] 取 Lambda 所在的类中的 this 指针。如果已经使用了 & 或者 = 就默认添加此选项。

代码示例:


void Widget::showString(char *pString)
{
qDebug() << pString;
} void Widget::testLambda()
{
int a = 1;
int b = 2; // [] 方式
qDebug() << "[] 方式";
[]()
{
// int x = a + b; // 会报错, 因为 a、b 不可见
qDebug() << "调用中:";
}(); // [&] 方式
qDebug() << "\n[&] 方式";
qDebug() << "调用前: " << "a:" << a << " b:" << b;
[&]()
{
a = 5;
b = 3;
qDebug() << "调用中: "<< "a:" << a << " b:" << b;
this->showString("[&] 方式使用成员函数 showString()");
}();
qDebug() << "调用后: " << "a:" << a << " b:" << b; // [=] 方式
qDebug() << "\n[=] 方式";
qDebug() << "调用前: " << "a:" << a << " b:" << b;
[=]()
{
// a = 5; // 会报错, 变量默认为只读属性, 需要使用 mutable 修饰符才可以修改
qDebug() << "调用中: "<< "a:" << a << " b:" << b;
this->showString("[] 方式使用成员函数 showString()");
}();
qDebug() << "调用后: " << "a:" << a << " b:" << b; // [=, &a] 方式
qDebug() << "\n[=, &a] 方式";
qDebug() << "调用前: " << "a:" << a << " b:" << b;
[=, &a]()
{
a = 16;
// b = 7; // 会报错, b变量默认为只读属性, 需要使用 mutable 修饰符才可以修改
qDebug() << "调用中: "<< "a:" << a << " b:" << b;
this->showString("[=, &a] 方式使用成员函数 showString()");
}();
qDebug() << "调用后: " << "a:" << a << " b:" << b; // [a] 方式
qDebug() << "\n[a] 方式";
qDebug() << "调用前: " << "a:" << a << " b:" << b;
[a]()
{
//a = 16; // 会报错, a变量默认为只读属性, 需要使用 mutable 修饰符才可以修改
//qDebug() << "调用中: "<< "a:" << a << " b:" << b; // 会报错, b 变量不可见
// this->showString("[a] 方式使用成员函数 showString()"); // 会报错, this 不可见
qDebug() << "调用中: "<< "a:" << a;
}();
qDebug() << "调用后: " << "a:" << a << " b:" << b; // [this] 方式
qDebug() << "\n[this] 方式";
qDebug() << "调用前: " << "a:" << a << " b:" << b;
[this]()
{
// qDebug() << "调用中: "<< "a:" << a << " b:" << b; //会报错 a、b 都不可见
this->showString("[this] 方式使用成员函数 showString()");
}();
qDebug() << "调用后: " << "a:" << a << " b:" << b; }

运行结果

[] 方式
调用中: [&] 方式
调用前: a: 1 b: 2
调用中: a: 5 b: 3
[&] 方式使用成员函数 showString()
调用后: a: 5 b: 3 [=] 方式
调用前: a: 5 b: 3
调用中: a: 5 b: 3
[] 方式使用成员函数 showString()
调用后: a: 5 b: 3 [=, &a] 方式
调用前: a: 5 b: 3
调用中: a: 16 b: 3
[=, &a] 方式使用成员函数 showString()
调用后: a: 16 b: 3 [a] 方式
调用前: a: 16 b: 3
调用中: a: 16
调用后: a: 16 b: 3 [this] 方式
调用前: a: 16 b: 3
[this] 方式使用成员函数 showString()
调用后: a: 16 b: 3

(参数表)

用于确定 Lambda 传入那些变量作为参数提供给 Lambda 函数体使用,如果没有可以省略 “()” 括号。

#include <iostream>

void onEvent(int type)
{
printf("onEvent:%d\n", type);
} void notice(void (*pfunc)(int type), int type)
{
pfunc(type);
} int main(int argc, char *argv[])
{
// 传统的方式, 回传参数
notice(onEvent, 5); // lambda 方式 一 , 回传参数
notice([](int type) {printf("Lambda :%d\n", type); }, 15); // lambda 方式 二 , 回传参数
auto func = [](int c, int d)
{
printf("AutoFunc: c: %d d:%d\n",c ,d);
}; // 调用
func(8, 9); return 0;
}

修饰符

如果没有修饰符则可以省略不写

mutable:

用于修饰 [外部变量访问方式说明符] 中列举的按值传递方式的变量时,可以修改其拷贝值。
相当于 void func(const int a, const int b) 变为 void func(int a, int b)
当有此修饰符时,引入的外部变量不能为空。

void Widget::testLambda()
{
int a = 1;
int b = 2; // mutable
qDebug() << "\nmutable";
qDebug() << "调用前: " << "a:" << a << " b:" << b;
[a, b] () mutable
{
a = 16; // 由于是值传递,a 其实是 lambda 函数体内的局部变量,所以修改的只是 a 的拷贝
b = 100;
qDebug() << "调用中: "<< "a:" << a << " b:" << b; // 会报错, b 变量不可见 }();
qDebug() << "调用后: " << "a:" << a << " b:" << b; }
mutable
调用前: a: 1 b: 2
调用中: a: 16 b: 100
调用后: a: 1 b: 2

exception:

说明 lambda 表达式是否抛出异常(noexcept),以及抛出何种异常,类似于void f()throw(X, Y)

->返回值类型

说明 lambda 函数体可以返回什么样类型的值,实际测试中似乎无论什么情况都可以省略,编译器会自动推测出类型。

#include <iostream>

void notice(int (*pfunc)(int a, int b), int aa, int bb)
{
printf("notice:%d\n",pfunc(aa, bb));
} int main(int argc, char *argv[])
{ // lambda, 回传参数
notice(
[](int a, int b)
{
printf("回调隐式返回类型: a:%d b:%d a+b:%d\n", a, b, a + b);
return a + b;
},
10, 15); puts(""); // lambda, 回传参数
notice(
[](int a, int b)->int
{
printf("回调显式返回类型: a:%d b:%d a+b:%d\n", a, b, a + b);
return a + b;
},
10, 15); puts(""); // lambda, 显式返回类型
auto func = [](int c, int d)->int
{
printf("显式返回类型: c:%d d:%d c+d:%d\n",c ,d, c + d);
return c + d;
}; // 调用
printf("func:%d\n", func(20, 10)); puts(""); // lambda, 隐式返回类型(视频中说只有一处才可以省略,但实际做的实验发现都可以)
auto func1 = [](int c, int d)
{
printf("隐式返回类型: c:%d d:%d c+d:%d\n", c, d, c + d);
if (c > 3)
return c + d;
else
return c;
}; // 调用
printf("func1:%d\n", func1(50, 10)); return 0;
}

{函数体}

实际具体的实现,一般都只有几句代码。

()

最后这一个括号其实代表的是调用的意思,当需要执行时,加上括号即可。


// 只是定义, 并未调用
[]()
{
printf("11111111111111\n");
}; // 定义了且被调用了
[]()
{
printf("11111111111111\n");
}(); // 最简单的 lambda 表达式
[] {puts("lambda..."); };

【学习笔记】QT从入门到实战完整版(Lambda)(2)的更多相关文章

  1. 2579页阿里P8Android学习笔记在互联网上火了,完整版开放下载

    笔记作者:来自于阿里P8级大神: Mark 笔记特点:条理清晰,理论+实战+源码,含图像化表示更加易懂. 内容概要:Android 相关,性能优化,Java 相关,Kotlin 相关,网络相关,插件化 ...

  2. Hadoop学习笔记(1) ——菜鸟入门

    Hadoop学习笔记(1) ——菜鸟入门 Hadoop是什么?先问一下百度吧: [百度百科]一个分布式系统基础架构,由Apache基金会所开发.用户可以在不了解分布式底层细节的情况下,开发分布式程序. ...

  3. iOS学习笔记-地图MapKit入门

    代码地址如下:http://www.demodashi.com/demo/11682.html 这篇文章还是翻译自raywenderlich,用Objective-C改写了代码.没有逐字翻译,如有错漏 ...

  4. tensorflow学习笔记二:入门基础 好教程 可用

    http://www.cnblogs.com/denny402/p/5852083.html tensorflow学习笔记二:入门基础   TensorFlow用张量这种数据结构来表示所有的数据.用一 ...

  5. 电子书下载:Delphi XE 5 移动开发入门手册(完整版)

    更多电子书请到: http://maxwoods.400gb.com 下载:Delphi XE5移动开发入门手册(完整版)

  6. spark学习笔记总结-spark入门资料精化

    Spark学习笔记 Spark简介 spark 可以很容易和yarn结合,直接调用HDFS.Hbase上面的数据,和hadoop结合.配置很容易. spark发展迅猛,框架比hadoop更加灵活实用. ...

  7. Android学习笔记(二十一)——实战:程序数据共享

    //此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 我们继续在Database项目的基础上继续开发,通过内容提供器来给它加入外部访问接口.首先将 MyDataba ...

  8. Android学习笔记(十五)——实战:强制下线

    //此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 实现强制下线功能的思路也比较简单,只需要在界面上弹出一个对话框, 让用户无法进行任何其他操作, 必须要点击对话 ...

  9. Android学习笔记(十二)——实战:制作一个聊天界面

    //此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 运用简单的布局知识,我们可以来尝试制作一个聊天界面. 一.制作 Nine-Patch 图片 : Nine-Pa ...

  10. SQL学习笔记——SQL初入门,Ubuntu下MySQL的安装

          刚开始接触sql,于是准备在Ubuntu下学习sql,就跟着itercast的sql教程开始入门了. 下面只是我个人的记录,高手请绕道: 一. 在安装之前,我们可以用下面这个命令通过开放端 ...

随机推荐

  1. 二叉搜索树 - C++ 实现

    二叉搜索树 - C++ 实现 概述 Overview 二叉查找树(英语:Binary Search Tree, 后文中简称 BST), 也称为二叉搜索树.有序二叉树(ordered binary tr ...

  2. oracle日常命令

    ---查询锁表(查出后,再执行查询结果进行释放:可多次查询,存在循环锁表的情况)-- select 'alter system kill session '|| ''''|| sess.sid || ...

  3. perl 通过<<和文件句柄将数据写入到文件中去

    可以通过文件句柄和<<运算符将文件内容写入到文件中去 #!usr/bin/perl -W use strict; use Spreadsheet::ParseExcel; use utf8 ...

  4. 刚哥谈架构(八)- 为你的应用选择合适的API

    前言: 架构师的主要活动是做出正确的技术决策.选择合适的API是一项重要的技术决策.那么今天就看看API的选择问题. 应用程序编程接口(API)是一种计算接口,它定义了多个软件中介之间的交互.它定义了 ...

  5. 使用Python实现多线程、多进程、异步IO的socket通信

    多线程实现socket通信服务器端代码 import socket import threading class MyServer(object): def __init__(self): # 初始化 ...

  6. 【HBase】简介、结构、数据模型、快速入门部署、shell操作、架构原理、读写数据流程、数据刷写、压缩、分割、Phoenix、表的映射、与hive集成、优化

    一.简介 1.定义 分布式.可扩展.支持海量数据存储的NoSQL数据库 2.数据模型 2.1逻辑结构 2.2物理存储结构 2.3数据模型介绍 Name Space:相当于数据库,包含很多张表 Regi ...

  7. Redis如何模糊匹配Key值

    Redis模糊匹配Key值 使用Redis的scan代替Keys指令: public Set<String> scan(String matchKey) { Set<String&g ...

  8. RocketMQ 在网易云音乐的实践

    本文作者:蒋星韬,网易云音乐服务端开发工程师. 云音乐线上场景众多,比如直播.评论.广告,各个业务线都会有消息场景比如发奖券,也会有延迟消息和事务消息场景,以及大数据做埋点数据.数据清洗.离线处理等. ...

  9. Jmeter 之 jp@gc - Stepping Thread Group

    jp@gc - Stepping Thread Group 自定义线程组,根据业务需要进行设计用户增加间隔时间等 1.  下载jmeter-plugins-manager-1.3.jar插件放入lib ...

  10. Redis的数据复制

    介绍 Redis 的复制 Redis 的复制功能分为同步(sync)和命令传播(command propagate)这两个操作 同步操作用于,将从服务器的数据库状态更新至主服务器当前所处的数据库状态: ...