Удаление объектов в Qt

Эта статья учит избегать утечек памяти и ошибок сегментирования при программировании на C++ с библиотекой Qt.

qt segmetation fault fuuu

Удаление объектов унаследованных от QObject

Qt самостоятельно заботится об удалении объектов, которые созданы с помощью оператора new, если класс объекта реализован по определенным правилам.

Правила:

  • Нужно унаследовать класс объекта от класса QObject.
  • Конструктор объекта должен задавать родителя объекта.
#include <QObject>
class Foo: public QObject
{
    Q_OBJECT

public:
    explicit Foo(QObject *parent = 0):
        QObject(parent)
    {
    }
}

int main()
{
    Foo *foo1 = new Foo();
    Foo *foo2 = new Foo(foo1);
    
    delete foo1;
    
    return 0;
}

В примере, объект foo1 является родителем объекта foo2. Объект foo2 называется дочерним по отношению к foo1.

При вызове delete foo1 сначала будет удален объект foo2, а затем foo1. Объект foo1 сам удаляет объект foo2.

Наличие или отсутствие деструктора в Foo не играет роли.

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

В документации Qt рекомендуется не использовать указатели дочерних объектов вне класса родителя. Так как после удаления родителя дочерний объект тоже перестает существовать.

Таким образом, можно создавать деревья объектов произвольной глубины. Qt удалит объекты согласно их иерархии. Можно не беспокоиться об утечках памяти и не создавать деструкторы.

Объекты в стеке

Объекты, которые создаются с помощью оператора new, называются динамическими, потому что создаются в динамической области памяти, которая называется кучей.

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

При создании дерева объектов в стеке, может произойти двойное удаление объекта, зависит от порядка создания объектов.

Пример правильного порядка следующий.

int main()
{
    QWidget window;
    QPushButton quit("Quit", &window);
}

Объекты quit и window создаются в стеке, и, согласно стандарту C++, среда выполнения сама вызовет деструкторы объектов в порядке обратном созданию объектов. То есть сначала будет удален объект quit, а затем window. Объект window не станет удалять объект quit, потому что последний прежде удалит себя из родителя (window).

Рассмотрим теперь не правильный порядок.


int main()
{
    QPushButton quit("Quit");
    QWidget window;

    quit.setParent(&window);
    ...
}

В этом примере сначала будет вызван деструктор window, который удалит объект quit. Затем среда выполнения попытается повторно удалить объект quit, что может привести к плачевным последствиям.

Удаление объектов из контейнера

Если контейнер содержит указатели на объекты, то все объекты можно удалить с помощью функции qDeleteAll(). Внутри функции используется оператор delete. Для использования функции нужно подключить заголовочный файл QtAlgorithms.

#include <QtAlgorithms>

class Foo {}

int main() {
    QHash<int, Foo *> hash;

    for (int i = 0; i < 10; i++) {
        hash[i] = new Foo();
    }

    qDeleteAll(hash);

    hash.clear();

    return 0;
}

В функцию можно передать любой контейнер Qt или не Qt-контейнер, но имеющий методы begin() и end(), которые возвращают итератор, поддерживающий префиксный инкремент (++a) и разыменовывание (*a).

При передаче в функцию контейнера, который содержит пары ключ-значение, будут удалены значения.

Функция не очищает контейнера. Чтобы очистить контейнер, нужно вызвать метод clear() контейнера.

Реализация функции qDeleteAll() довольно проста:

template <typename ForwardIterator>
void qDeleteAll(ForwardIterator begin, ForwardIterator end)
{
    while (begin != end) {
        delete *begin;
        ++begin;
    }
}

template <typename Container>
inline void qDeleteAll(const Container &c)
{
    qDeleteAll(c.begin(), c.end());
}

Ссылки

  1. Описание деструктора QObject в официальной документации Qt на английском.
  2. Описание функции qDeleteAll() в официальной документации Qt на английском.
  3. Деревья объектов в официальной документации Qt на английском.

Автор

Дмитрий Бравиков

Инженер. Электронщик. Программист.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Google photo

Для комментария используется ваша учётная запись Google. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.