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. 怎么样子盒子能撑起父盒子?浮动,BFC,边距重叠

    怎么样子盒子能撑起父盒子? 从行内元素跟块元素来看: 一般情况下,行内元素只能包含数据和其他行内元素. 而块级元素可以包含行内元素和其他块级元素. 块级元素内部可以嵌套块级元素或行内元素. 建议行内元 ...

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

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

  3. 利用KubeEdge在A500部署边缘推理任务

      利用KubeEdge在A500部署边缘推理任务 目  录 1 环境介绍... 1 2 云端环境部署... 2 2.1 在master节点安装Docker和k8S (ubuntu) 2 2.1.1 ...

  4. 【云原生 · Kubernetes】部署Kubernetes集群

    [云原生 · Kubernetes]搭建Harbor仓库 接着上次的内容,后续来了! 在master节点执行脚本k8s_master_install.sh即可完成K8S集群的部署,具体步骤参考如下(1 ...

  5. [C# 中的序列化与反序列化](.NET 源码学习)

    [C# 中的序列化与反序列化](.NET 源码学习) 关键词:序列化(概念与分析)    三种序列化(底层原理 源码)    Stream(底层原理 源码)    反射(底层原理 源码) 假如有一天我 ...

  6. day01-计算机的本质

    计算机的本质 计算机又称为"电脑": 通电的大脑 意味着我们人类希望计算机通电之后可以跟人脑一样思考问题.解决问题 计算机存储数据的本质 计算机是基于电工作,而电信号只有高低电平两 ...

  7. win7修改开机动画

    开机动画的修改 首先win7的过场动画是存在于C:\Windows\System32\bootres.dll ,而修改过程动画就需要修改这个dll,我不会改,所以只能用工具美化大师,软媒魔方里面的一个 ...

  8. MISC相关刷题记录迁移

  9. NOIP 口胡

    因为没准备啥东西 这两天口胡一下近年 NOIP 的题 大概会一道不落?没什么很寄的考点主要是 2021 T1 报数 打一个 \(O(\log n)\) 查询 \(n\) 中是否有 \(7\),打一个类 ...

  10. Day30.1:Math的常用方法

    Math 1.1 Math概述 Math类在Java.lang包下,不需要导包 public final class Math extends Object Math含有基本的数字运算方法,没有构造器 ...