【学习笔记】QT从入门到实战完整版(Lambda)(2)

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 

void onEvent()
{
	printf("onEvent\n");
}

void notice(void (*pfunc)())
{
	pfunc();
}

int main(int argc, char *argv[])
{
	notice(onEvent);
	return 0;
}

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

#include 

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 

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 

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)