Подтвердить что ты не робот

Почему официальные примеры Qt и учебные пособия не используют интеллектуальные указатели?

Почему официальные примеры и обучающие материалы о Qt-библиотеке никогда не используют интеллектуальные указатели? Я вижу только new и delete для создания и уничтожения виджетов.

Я искал логическое обоснование, но я не мог его найти, и сам не вижу его, кроме случаев, когда это связано с историческими причинами или обратной совместимостью: не все хотят, чтобы программа завершилась, если конструктор виджета вышел из строя, и обработал его через try/catch блоки просто уродливы (даже если они используются в нескольких местах). Тот факт, что родительские виджеты могут взять на себя ответственность за детей, также лишь частично объясняет мне эту вещь, поскольку вам все равно придется использовать delete для родителей на некотором уровне.

4b9b3361

Ответ 1

Поскольку Qt полагается на родительско-дочернюю модель для управления ресурсами Qobject. Он следует за композитным шаблоном "Цепь ответственности", который используется для управления событиями, управления памятью, рисования, обработки файлов и т.д.

На самом деле, попытка использования QObject в shared\unique указателе является перестройкой (99% времени).

  • Вы должны указать пользовательский удаляющий элемент, который будет вызывать deleteLater
  • Ваш qobject с родителями уже имеет ссылку в родительском объекте. Значит, вы знаете, что объект не просачивается, пока существует родитель. Когда вам нужно избавиться от него, вы можете напрямую вызвать deleteLater.
  • В QWidget без родителя уже есть ссылка в объекте Qapplication. Так же, как и в пункте 2.

Тем не менее, вы все равно можете использовать RAII с Qt. Например, QPointer ведет себя как слабая ссылка на QObject. Я использовал бы QPointer<QWidget>, а не QWidget*.

Примечание: не слишком звучать фанбой, два слова: Qt + valgrind.

Ответ 2

Интеллектуальные указатели на дочерние элементы

Классы интеллектуального указателя std::unique_ptr и std::shared_ptr предназначены для управления памятью. Наличие такого умного указателя означает, что вы владеете указателем. Однако при создании QObject или производного типа с родителем QObject право собственности (ответственность за очистку) передается родительскому QObject. В этом случае стандартные интеллектуальные указатели библиотеки не нужны или даже опасны, поскольку они могут потенциально вызвать двойное удаление. Хлоп!

Необработанные указатели для сирот

Однако, когда a QObject (или производный тип) создается в куче без родительского QObject, все очень отличается. В этом случае вам не следует просто держать необработанный указатель, но умный указатель, желательно std::unique_ptr для объекта. Таким образом вы получаете безопасность ресурсов. Если позже вы передадите собственность объекта родительскому QObject, вы можете использовать std::unique_ptr<T>::release(), например:

auto obj = std::make_unique<MyObject>();
// ... do some stuff that might throw ...
QObject parentObject;
obj->setParent( &parentObject );
obj.release();

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

В более общем примечании

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

Ответ 3

Вы уже ответили на свой вопрос: except if it for historic reasons/backward compatibility. Библиотека, которая столь же огромна, как QT, не может предположить, что все, кто использует библиотеку, имеют компиляторы, которые поддерживают С++ 11. new и delete гарантированы в прежних стандартах.

Однако, если у вас есть поддержка для использования интеллектуальных указателей, я бы рекомендовал использовать их поверх необработанных указателей.

Ответ 4

В дополнение к тому, что сказал @Jamey:

Если вы продублируете его с умом, вам никогда не придется использовать удаление в виджетах. Допустим, у вас есть главное окно, и вы создаете его автообъект и запускаете это окно в цикле событий. Теперь остальные элементы этого виджета можно добавить в качестве своих дочерних элементов. И поскольку вы добавляете их в MainWindow прямо/косвенно в качестве дочернего элемента, когда вы закроете это главное окно, все будет позаботиться автоматически. Просто вы должны убедиться, что все динамические объекты/виджеты, которые вы создали, являются дочерними/внуками MainWindow. Следовательно, нет необходимости в явном удалении.

Ответ 5

  • QObject имеет определенный родительский элемент, а структура дерева программа позволяет эффективно управлять памятью.

  • Динамизм в Qt нарушает этот прекрасный идеал, например. передавая необработанный указатель. Можно легко закончить, удерживая dangling pointer, но это общая проблема при программировании.

  • Умный указатель Qt, на самом деле слабая ссылка, QPointer<T>
    и предоставляет некоторые из конфет STL.

  • Можно также смешивать с std::unique_ptr и тому подобное, но оно должно используется только для не-Qt-механизмов в вашей программе.