将 lambda 函数作为第三个参数传递给 QObject::connect 时出错

Posted

技术标签:

【中文标题】将 lambda 函数作为第三个参数传递给 QObject::connect 时出错【英文标题】:Error passing a lambda function as 3rd argument to QObject::connect 【发布时间】:2015-11-05 00:02:36 【问题描述】:

我正在尝试通过将 lambda 函数作为第三个参数传递来调用 QObject::connect

但是,我 Visual Visual Studio 给了我以下错误:

在 0x0000000066B48265 (Qt5Cored.dll) 中未处理的异常 QCustomPlotInVS_FirstTry.exe:0xC0000005:读取访问冲突 位置 0x0000000000000008。

这是发生错误时调用堆栈上的前 3 行:

Qt5Cored.dll!QListData::size() 第 91 行 QCustomPlotInVS_FirstTry.exe!QList::size() 第 132 行 QCustomPlotInVS_FirstTry.exe!QCustomPlot::graph(int index) 第 9657 行

qList.h 中的第 91 行是: inline int size() const return d->end - d->begin;

我认为我收到此错误是因为我错误地尝试将指针(即QCustomPlot* plot)与 lambda 函数一起使用。

我的 lambda 函数语法是否正确? 如果没有,我做错了什么?

这是我用 lambda 函数调用 QObject::connect 的函数:

void setupRealTimePlot(QCustomPlot* plot, QTimer* dataTimer)

    plot->addGraph(); // blue line
    plot->graph(0)->setPen(QPen(Qt::blue));
    plot->graph(0)->setBrush(QBrush(QColor(240, 255, 200)));
    plot->graph(0)->setAntialiasedFill(false);
    plot->addGraph(); // red line
    plot->graph(1)->setPen(QPen(Qt::red));
    plot->graph(0)->setChannelFillGraph(plot->graph(1));

    plot->addGraph(); // blue dot
    plot->graph(2)->setPen(QPen(Qt::blue));
    plot->graph(2)->setLineStyle(QCPGraph::lsNone);
    plot->graph(2)->setScatterStyle(QCPScatterStyle::ssDisc);
    plot->addGraph(); // red dot
    plot->graph(3)->setPen(QPen(Qt::red));
    plot->graph(3)->setLineStyle(QCPGraph::lsNone);
    plot->graph(3)->setScatterStyle(QCPScatterStyle::ssDisc);

    plot->xAxis->setTickLabelType(QCPAxis::ltDateTime);
    plot->xAxis->setDateTimeFormat("hh:mm:ss");
    plot->xAxis->setAutoTickStep(false);
    plot->xAxis->setTickStep(2);
    plot->axisRect()->setupFullAxesBox();

    // make left and bottom axes transfer their ranges to right and top axes:
    QObject::connect(plot->xAxis, SIGNAL(rangeChanged(QCPRange)), plot->xAxis2, SLOT(setRange(QCPRange)));
    QObject::connect(plot->yAxis, SIGNAL(rangeChanged(QCPRange)), plot->yAxis2, SLOT(setRange(QCPRange)));

    // setup a timer that repeatedly calls MainWindow::realtimeDataSlot:
    QObject::connect(dataTimer, &QTimer::timeout, 
        [&]()
        
            // calculate two new data points:
            double key = QDateTime::currentDateTime().toMSecsSinceEpoch()/1000.0;
            static double lastPointKey = 0;
            if (key-lastPointKey > 0.01) // at most add point every 10 ms
            
            double value0 = qSin(key); //qSin(key*1.6+qCos(key*1.7)*2)*10 + qSin(key*1.2+0.56)*20 + 26;
            double value1 = qCos(key); //qSin(key*1.3+qCos(key*1.2)*1.2)*7 + qSin(key*0.9+0.26)*24 + 26;
            // add data to lines:
            plot->graph(0)->addData(key, value0);
            plot->graph(1)->addData(key, value1);
            // set data of dots:
            plot->graph(2)->clearData();
            plot->graph(2)->addData(key, value0);
            plot->graph(3)->clearData();
            plot->graph(3)->addData(key, value1);
            // remove data of lines that's outside visible range:
            plot->graph(0)->removeDataBefore(key-8);
            plot->graph(1)->removeDataBefore(key-8);
            // rescale value (vertical) axis to fit the current data:
            plot->graph(0)->rescaleValueAxis();
            plot->graph(1)->rescaleValueAxis(true);
            lastPointKey = key;
            
            // make key axis range scroll with the data (at a constant range size of 8):
            plot->xAxis->setRange(key+0.25, 8, Qt::AlignRight);
            plot->replot();

            // calculate frames per second:
            static double lastFpsKey;
            static int frameCount;
            ++frameCount;
            if (key-lastFpsKey > 2) // average fps over 2 seconds
            
            lastFpsKey = key;
            frameCount = 0;
            
        );

    dataTimer->start(0); // Interval 0 means to refresh as fast as possible


【问题讨论】:

【参考方案1】:

您在稍后调用的闭包中通过引用(初犯:plot)捕获堆栈分配的变量;一旦你的闭包被调用,所有这些东西都会变成垃圾。而是按值捕获。

【讨论】:

你的答案是正确的。我将[&]()//... 更改为[=]()//...,它起作用了。但是,我不清楚为什么。我想如果我按值捕获它会创建变量的副本(在我的情况下为QCustomPlot* plot),那么对副本的任何更改都不会应用于原始变量。我想我刚刚意识到我的担心是错误的,因为复制指针会给出一个新变量,该变量仍然指向我要修改的对象。我的解释似乎正确吗? 没错。您可能会更容易将引用解析为指针:通过引用捕获局部指针变量就像将指向它的指针存储在双指针中一样。当您稍后尝试使用它时,它指向一个不再存在的局部变量。相反,按值捕获它就像创建一个新的QCustomPlot * 指向原始对象(指针的值是指向对象的地址)。

以上是关于将 lambda 函数作为第三个参数传递给 QObject::connect 时出错的主要内容,如果未能解决你的问题,请参考以下文章

如何将 lambda 表达式作为参数传递给 c++ 模板

如何使用 GET 请求将参数传递给 AWS Lambda 函数?

lambda

如何将数组的值作为第二个参数传递给 awk 的 split 函数?

将对象的属性作为第二个参数传递给jQuery函数本身

如何将列名作为参数传递给 dplyr 中的函数?