Qt: Как правильно и безопасно написать деструктор этого класса?

Я использую Qt5 на Windows7, и я недавно нашел интересный пример кода Qt.

В основном это выглядит так:

ButtonWidget::ButtonWidget(const QStringList &texts, QWidget * parent)
: QWidget(parent)
{
signalMapper = new QSignalMapper(this);

QGridLayout * gridLayout = new QGridLayout;
for (int i = 0; i < texts.size(); ++i)
{
QPushButton * button = new QPushButton(texts[i]);
connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(button, texts[i]);
gridLayout->addWidget(button, i / 3, i % 3);
}

connect(signalMapper, SIGNAL(mapped(QString)), this, SIGNAL(clicked(QString)));

setLayout(gridLayout);
}

Это хороший и полезный пример, но у него нет правильного деструктора … на всякий случай, если я хочу удалить объект ButtonWidget введите, или если я хочу настроить код, чтобы иметь возможность удалять / добавлять виджеты. Идея состоит в том, чтобы удалить все объекты, созданные в конструкторе (динамически, используя new).

Мой подход состоял в том, чтобы использовать приватную переменную QList<QPushButton*> list, добавьте все новые назначенные кнопки в список (в конструкторе) и удалите их один за другим в деструкторе, используя приведенный выше list, Но, похоже, детский сад — подход.

Я думаю, что должен быть какой-то другой, лучший способ сделать это, без списка и без суеты кода конструктора 🙂 Спасибо за ваше время и терпение!

2

Решение

С классами Qt, которые получают parent в качестве аргументов своих конструкторов, например, QSignalMapper, важно отметить, что класс добавляет себя в список объектов своего родителя, и он будет уничтожен, когда его родитель (QObject) будет уничтожен.

Поэтому, если вы передаете объект родителю, вам не нужно ничего делать. В вашем cpp-файле может быть пустой деструктор, просто чтобы убедиться, что определение члена доступно для QObject, но это зависит от класса.

Однако, если вы решите написать свой собственный деструктор, типичный «потомок» Qt, который реализован правильно, при удалении удалит себя из своего родителя.

В дополнение к вопросу, поставленному в заголовке / заголовке, ОП спросил, что произойдет, если он захочет удалить подобъект до удаления родителя:

Прицельная следующий, кажется (и это все, что я знаю), что он может просто удалить дочерние виджеты, и они удалят себя. Было упомянуто, что можно установить родителя дочернего элемента равным нулю, но это необязательно, за исключением случаев, когда нужно удалить родителя и сохранить ребенка живым (перевоспитание …).

Однако, как показано в последнем абзаце пример, порядок создания экземпляров важен. Если родительский объект создается после дочернего объекта, а родительский родительский объект установлен явно, у дочернего элемента останется мертвая ссылка / недопустимый указатель на родительский элемент, и при уничтожении дочернего элемента при попытке удаления самого себя произойдет неопределенное поведение. родитель, выходящий за рамки видимости.

В дополнение к Qt’s документация, Можно также посмотреть на реализацию QObject :: setParent здесь (setParent_helper). Из этого видно, что они прилагают большие усилия, чтобы удаление детей / родителей происходило без сбоев, за исключением упомянутого случая.

5

Другие решения

Вы должны следить за тем, чтобы все ваши виджеты подключались к Qt дерево объектов, который позаботится о разрушении для вас. Вы можете сделать это, предоставив ему родителя при его создании. Как вы сделали с SignalMapper,

3

От QWidget::setLayout:

QWidget станет владельцем макета.

От QLayout::addItem (вызывается QLayout::addWidget):

Примечание. Право собственности на элемент передается макету, и макет несет ответственность за его удаление.


Вам не нужно ничего убирать. Управляйте виджетами через макет
(addWidget, removeWidget/removeItem).

3

Это не правда, что нет «правильного» деструктора. Там является деструктор — сгенерированный компилятором — и этот деструктор делает все необходимое для освобождения ресурсов. Так и должно быть, и именно так должен разрабатываться современный код C ++.

Правильно спроектированные классы C ++ должны использоваться без явного управления их ресурсами. Это тот случай, здесь.

Кроме того, пример излишне динамически распределяет членов, которые могли быть просто членами класса. В C ++ 11 также нет необходимости в преобразователе сигналов. Вот как я это сделаю:

class ButtonWidget : public QWidget {
Q_OBJECT
QGridLayout m_layout { this };
public:
ButtonWidget(const QStringList &items, QWidget *parent = 0);
~ButtonWidget();
Q_SIGNAL void buttonClicked(const QString &);
}

ButtonWidget::ButtonWidget(const QStringList &items, QWidget *parent)
: QWidget(parent)
{
const int columns = 3;
for (int i = 0; i < items.size(); ++i) {
auto text = items[i];
auto button = new QPushButton(text);
connect(button, &QPushButton::clicked, [this, text]{
emit buttonClicked(text);
});
m_layout.addWidget(button, i / columns, i % columns);
}
}

ButtonWidget::~ButtonWidget() {}

Это полный, удобный виджет без утечек памяти. Вот так должен выглядеть современный C ++ / Qt. Если вам нужно сделать что-то причудливое в деструкторе, вы всегда должны рассмотреть возможность выделения управления памятью в собственный класс RAII. Например, вместо того, чтобы вручную закрывать дескриптор файла в деструкторе, рассмотрите использование QFileили написание аналогичного класса управления ресурсами, который затем можно использовать, не беспокоясь о ручном управлении временем жизни дескриптора.

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector