Страницы

Поиск по вопросам

четверг, 9 января 2020 г.

connect с указателями в контейнере

#cpp #qt #qt5


При соединении с указателями, которые находятся внутри локального map, вылетает программа
с ошибкой: Segmentation fault. Если сделаю мап статик или как поле, то все будет работать.

Вопрос: почему ломается коннект, если map хранит указатели на объекты, а не сами
объекты? Какие решение есть, кроме как статик?

Вот пример, который смог сделать:


someobj.h

#ifndef SOMEOBJ_H
#define SOMEOBJ_H

#include 
#include 

class SomeObj : public QObject
{
    Q_OBJECT
public:
    explicit SomeObj(QObject *parent = nullptr) : QObject(parent) { }
public slots:
    void someSlot() { qDebug() << "someSlot"; }
};

#endif // SOMEOBJ_H


mainwindows.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include "someobj.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
    SomeObj obj[7];
};

#endif // MAINWINDOW_H


mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include 

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QMap m {
        { ui->pushButton, &obj[0] },
        { ui->pushButton_2, &obj[1] },
        { ui->pushButton_3, &obj[2] },
        { ui->pushButton_4, &obj[3] },
        { ui->pushButton_5, &obj[4] },
        { ui->pushButton_6, &obj[5] },
        { ui->pushButton_7, &obj[6] }
    };
    for (auto it = m.begin(); it != m.end(); ++it) {
        connect( it.key(), &QPushButton::clicked, [=] () {
            it.value()->someSlot();
            qDebug() << it.key()->objectName();
            ui->btnStop->setEnabled(true);
        });
    }
}

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




UPD1: а почему нельзя вызвать sender() в анонимной функции?



UPD2: При раскомменчивании любой строчки программа вылетает.

for (auto it = m.begin(); it != m.end(); ++it) {
    connect( it.key(), &QPushButton::clicked, [=, obj = it.value(), btn = it.key()] () {
//            obj->someSlot();                      //  #1
//            qDebug() << sender()->objectName();   //  #2
        qDebug() << obj->objectName();          // работает
        qDebug() << btn->objectName();          // работает
        ui->btnStop->setEnabled(true);
    });
}




UPD3:
При попытке:

connect( it.key(), &QPushButton::clicked, [obj = it.value(), btn = it.key(), ui] () {...


вылетает ошибка 'ui' in capture list does not name a variable. Поэтому так:

connect( it.key(), &QPushButton::clicked, [obj = it.value(), btn = it.key(), uii
= ui] () {
//        obj->someSlot();                      //  #1
//        qDebug() << sender()->objectName();   //  #2
    qDebug() << obj->objectName();          // работает
    qDebug() << btn->objectName();          // работает
    uii->btnStop->setEnabled(true);
});


И так, уже функция sender() не видна, тк this не передается. Но почему при раскомменчивании
#1 программа вылетает?



UPD4: все договариваю. Могу проект скинуть. Класс SomeObj не изменял.Конструктор
MainWindow изменил так:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QMap m {
        { ui->pushButton, &obj[0] },
        { ui->pushButton_2, &obj[1] },
        { ui->pushButton_3, &obj[2] },
        { ui->pushButton_4, &obj[3] },
        { ui->pushButton_5, &obj[4] },
        { ui->pushButton_6, &obj[5] },
        { ui->pushButton_7, &obj[6] }
    };
    for (auto it = m.begin(); it != m.end(); ++it) {

        connect( it.key(), &QPushButton::clicked, [obj = it.value(), btn = it.key(),
btnS = this->ui->btnStop] () {
//            obj->someSlot();                      //  #1
//            qDebug() << sender()->objectName();   //  #2
            qDebug() << obj->objectName();          // работает
            qDebug() << btn->objectName();          // работает
            btnS->setEnabled(true);
        });
    }
}

    


Ответы

Ответ 1



"Теоретически" ваш map хранит указатели на интересующие вас объекты и в правильно написанном коде время жизни самого map не должно влиять на работоспособность вашего кода. Однако вы написали вашу лямбда-функцию так, что она занимается захватом итераторов этого map и всю работу делает через эти итераторы. В такой ситуации, как только map прекращает свое существование, попытки доступа через её итераторы приводят к падению программы. Прекратите захватывать итераторы и перепишите свою лямбду так, чтобы она захватывала значения указателей it.key() и it.value() и далее работала именно через захваченные указатели, а не через итератор. Таким образом вы полностью отвяжете вашу лямбду от этого временного map [=, obj = it.key(), button = it.value()] () { obj->someSlot(); qDebug() << button->objectName(); ui->btnStop->setEnabled(true); } Более того, при создании долгоживущих лямбд я бы посоветовал вам не пользоваться механизмом неявного захвата. Лучше прописывайте все захваты явно, чтобы отдавать себе отчет в том, что именно вы захватываете и к каким последствиям это может привести [obj = it.key(), button = it.value(), ui = this->ui] () { obj->someSlot(); qDebug() << button->objectName(); ui->btnStop->setEnabled(true); }

Комментариев нет:

Отправить комментарий