Qt5 lambda表达式连接QTcpServer信号槽

时间:2021-12-05 19:15:28

Problem

当有多条语句调用而又不希望写成一个单独的模块(函数或者方法)时,对于普通的方法可以写成内联形式,避免函数调用入栈、出栈等开销,也或者可以定义一段宏,不过宏没有类型检查,也没有作用对象的概念。而当这多条语句是在定义QObject::connect连接某信号的槽的实现逻辑时,除了lambda表达式,好像没有别的更好方法了。

Solution

先上一段代码,用一个简单的Qt自带的QTcpSocket和QTcpServer实现一个Tcp单机收发的功能。

头文件MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <iostream>
#include <cstdio>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <windows.h>
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();

private slots:
void on_BtnTcpSndData_clicked();

private:
Ui::MainWindow *ui;
QTcpServer tcpServer;
QTcpSocket socket;
};

#endif // MAINWINDOW_H

源文件MainWindow.cpp

#include "MainWindow.h"
#include "ui_MainWindow.h"

using namespace std;

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
connect(&tcpServer,&QTcpServer::newConnection,
[&](){QTcpSocket* pSocket=tcpServer.nextPendingConnection();
connect(pSocket,&QTcpSocket::readyRead,
[=](){ui->textEditTcpRcv->append(QString::fromLocal8Bit(pSocket->readAll())); });});
tcpServer.listen(QHostAddress("127.0.0.1"),8888);
Sleep(500);
socket.connectToHost("127.0.0.1",8888);
ui->setupUi(this);
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::on_BtnTcpSndData_clicked()
{
socket.write(ui->textEditDataToSnd->toPlainText().toLocal8Bit().data());
}

注意下在pro文件中对c++11、和network进行设置。pro文件中添加如下2条语句。

QT       += network
CONFIG += c++11

如上代码所示,直接在构造函数中即可实现所有的TcpSocket、TcpServer创建与连接工作,而且将Server端接收到数据的处理逻辑也写进去了。所有的逻辑都在下面这条语句。

    connect(&tcpServer,&QTcpServer::newConnection,
[&](){QTcpSocket* pSocket=tcpServer.nextPendingConnection();
connect(pSocket,&QTcpSocket::readyRead,
[=](){ui->textEditTcpRcv->append(QString::fromLocal8Bit(pSocket->readAll())); });});

这是一个嵌套的connect连接操作。首先,如果Server端有新的连接,进入第一个lambda表达式的处理逻辑,保存当前连接的套接字,将当前套接字的准备好读的信号连接到最终处理的也即第二个lambda表达式。第二个lambda表示式中将当前套接字读取的内容显示在屏幕上。

注意tcpServer.nextPendingConnection()返回的指针是多变的,即第一次取出是指针,未有新连接,再调用些函数取出的是空指针。所以需要用一个自定义的变量来进行保存。
Qt5 lambda表达式连接QTcpServer信号槽

———————————————————————————

Appendix Qt Lambda表达式

此介绍出处http://www.aichengxu.com/view/5170676

简单来说,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,引用传递方式捕捉其它所有变量。

不过值得注意的是,捕捉列表不允许变量重复传递。

——————————————————————–保持成长! :)