【问题标题】:Trouble using C++11 lambda function to connect a click event to a function使用 C++11 lambda 函数将点击事件连接到函数时遇到问题
【发布时间】:2016-07-11 22:26:33
【问题描述】:

在一个 GUI 应用程序中,我使用了很多按钮。这些按钮标记为pbxx,其中xx 是网格布局中按钮的行号和列号。按下按钮时,需要突出显示。今天我读到了 lambda 函数 Brian PoteatKuba Ober 并认为我会尝试在我的代码中实现它。

在我的 GuiDisplay 类(继承 QMainWindow 的类)中,我有一个函数叫做:

make_connections(); 

这将使我所有的按钮连接(所有信号都连接到单个插槽on_pushbutton_clicked()。我在这里添加了代码:

GuiDisplay 类

class GuiDisplay : public QMainWindow
{
    Q_OBJECT

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

    ... Some more public functions ...

    /**
     * @brief Connects all custom signals and slots.
     */
    void make_connections();

    /**
     * @brief Will highlight the provided pushbutton with provided rgb colour
     */
    void highlight_pushbutton(QPushButton &pb, const int rgb[]);

private slots:
    void on_pushbutton_clicked(const QString& label);

private:
    Ui::GuiDisplay *ui;
};

GuiDisplay类的make_connections函数

void GuiDisplay::make_connections()
{
    // Loop through all the pushbuttons and connect clicked signal to clicked slot
    QString pb_label = "";
    for(int i = 0; i < 8; ++i)
    {
        for(int j = 0; j < 8; ++j)
        {
            // Find the pushbutton
            pb_label = "pb" + QString::number(i) + QString::number(j);
            QPushButton* pb_ptr = this->findChild<QPushButton*>(pb_label);
            //connect(pb_ptr, SIGNAL(clicked()), this, SLOT(on_pushbutton_clicked()));
            connect(pb_ptr, &QPushButton::clicked, [this]{on_pushbutton_clicked(pb_label);});
        }
    }
}

连接时出现问题

connect(pb_ptr, &QPushButton::clicked, [this]{on_pushbutton_clicked(pb_label);});

构建给了我以下错误

'pb_label' is not captured

所以我认为可以,那么执行以下操作似乎并没有错:

connect(pb_ptr, &QPushButton::clicked, [this, &pb_label]{on_pushbutton_clicked(pb_label);});

构建时的错误消失了,但是每当执行此代码时,我的 GUI 应用程序就会意外崩溃。不知道为什么。这里有什么帮助吗?

【问题讨论】:

  • 按值捕获pb_label,即[this, pb_label]
  • 或者你可以使用[=]

标签: c++ qt c++11 lambda


【解决方案1】:

您的错误是由于使用了对临时的引用。 pb_label 仅在 GuiDisplay::make_connections 期间存在,您的 lambda 由 Qt 存储,并且在每次触发信号时都会被引用。发生这种情况时,您将引用一个已销毁的对象。

所以正确的连接方式是:

connect(pb_ptr, &QPushButton::clicked, [=]{ on_pushbutton_clicked(pb_label); });

从 Qt 5.2 开始,添加了 QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection) 重载,它将在对象生命周期结束时自动断开连接。在这种情况下,连接将是:

connect(pb_str, &QPushButton::clicked, this, [=]{ on_pushbutton_clicked(pb_label); });

也就是说,当你可以简单地这样做时,你不应该在这里使用 lambda:

connect(pb_ptr, &QPushButton::clicked, this, &GuiDisplay::on_pushbutton_clicked);

然后修改您的GuiDisplay::on_pushbutton_clicked 以不接受任何参数。您可以通过调用sender() 来获取GuiDisplay::on_pushbutton_clicked 中的对象名称:

返回指向发送信号的对象的指针

然后只需使用QObject::objectName 获取pb_label

此属性保存此对象的名称

因此,您只需将 pb_label 替换为:sender()-&gt;objectName()

直接槽连接优于 lambda 有几个原因:

  1. 它不再需要为pb_label 存储一个临时的 lambda 一个临时的 QString
  2. 它消除了需要额外堆栈帧的 lambda 间接
  3. 调试到 lambdas 并不总是对开发人员友好,尽管一些 IDE 可以缓解这个问题
  4. Prior to Qt 5.2 there was no way to disconnect a lambda slot if objects it depended upon were destroyed:

“接收器”被销毁时不会自动断开连接,因为它是一个没有QObject 的函子。

【讨论】:

  • 我认为值得一提的是,假设使用的是 Qt 5.2 或更高版本,如果您需要在某些对象被销毁时自动断开连接,您可以将该对象作为context object 传递。
  • @thuga 公平点,实际上我什至从我引用的 Qt wiki 中审查了这一点。我从来没有费心从 Qt 5.1 升级,而且我一直在没有人使用 Qt 5.2 的荒谬心态下工作。我已经更新了答案。
【解决方案2】:

感谢 Piotr Skotnicki 和 Simple,您的两个建议都有效。我可能应该进一步阅读,我会在捕获列表中看到 [=](请参阅:Lambda

所以,我导致问题的一行代码可以是:

connect(pb_ptr, &QPushButton::clicked, [this, pb_label]{on_pushbutton_clicked(pb_label);});

connect(pb_ptr, &QPushButton::clicked, [=]{on_pushbutton_clicked(pb_label);});

【讨论】:

  • FWIW,当它被引用捕获时,[this, &amp;pb_label],然后原始对象(捕获的引用指向)在退出 make_connections 时被销毁,这就是你遇到崩溃的原因
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-17
  • 2018-11-21
相关资源
最近更新 更多