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

127
05 сентября 2019, 04:00

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

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

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

someobj.h

#ifndef SOMEOBJ_H
#define SOMEOBJ_H
#include <QObject>
#include <QDebug>
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 <QMainWindow>
#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 <QDebug>
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QMap<QPushButton *, SomeObj *> 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<QPushButton *, SomeObj *> 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);
        });
    }
}
Answer 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);
}
READ ALSO
Птичий язык внутри метода если передать в него строку с кириллицей?

Птичий язык внутри метода если передать в него строку с кириллицей?

В юнит-тестах на java+spring передаю в метод строку с русскими буквамиВнутри метода в параметр приходит уже не то, что записал, например статус...

119
Libgdx. Создание колизии

Libgdx. Создание колизии

Создаю 2д стрелялку на LibgdxЕсть спрайт космического корабля и снаряда

130
Как работает HashMap поиск элемента по хешу?

Как работает HashMap поиск элемента по хешу?

О том как работает HashMap в принципе все понятноЧто такое бандлы, сложность поиска и тд

119
Preference Framework ввод чисел

Preference Framework ввод чисел

Как создавать кастомные элементы Preference Framework? Например, ввод целых или вещественных чисел с такой своеобразной "прокруткой", или например...

111