Современный C++ (11/14/17/20…) настойчиво учит нас: «Забудьте про new и delete, используйте умные указатели». Это отличный совет для чистого C++, но как только вы открываете документацию Qt, на вас снова прыгают T*.
Почему даже в 2026 году невозможно написать серьезное приложение на Qt, используя исключительно умные указатели? Давайте разберемся, где «умный» код ломает логику фреймворка.
1. Конфликт систем владения: иерархия QObject vs Smart Pointers
Фундаментальная особенность Qt — автоматическое управление памятью через иерархию объектов. Передавая указатель на родителя в конструктор QObject (или его наследников, например, QWidget), вы делегируете управление временем жизни объекта этому родителю.
Проблема двойного удаления (Double Free) Ошибка возникает, когда QObject, имеющий родителя, помещается в умный указатель (например, std::unique_ptr или QScopedPointer). В этом случае возникают два независимых владельца:
Родительский объект, который удалит потомка в своем деструкторе или при вызове
delete later().Умный указатель, который ничего не знает об иерархии Qt и попытается удалить объект при выходе из области видимости.
В результате, когда одна из систем удаляет объект первой, вторая остается с «висячей» ссылкой (dangling pointer). Повторная попытка освобождения памяти приведет к аварийному завершению программы (Segmentation fault).
Тут надо заметить что родительский QObject узнает об удаление своего дочернего объекта, и двойного удаления не случится. А вот если объект удалиться через родителя, то умный указатель никак не сможет узнать об этом и произойдет тот самый Double Free.
2. Динамическое дерево объектов и QPointer
Если вам нужно хранить ссылку на объект QObject, которым вы не владеете, и который может удалиться в любое время, то по классике нам нужно использовать QWeakPointer. Но он работает только с QSharedPointer, который, как мы уже выяснили, использовать нельзя.
Здесь на сцену выходит QPointer<T>. Это уникальный для Qt «слабый» указатель:
Он не владеет объектом.
Он автоматически обнуляется (
null), когда целевойQObjectудаляется (черезdeleteили родителем).Это возможно благодаря подписке на сигнал
destroyed().
Без понимания работы сырых указателей «под капотом» и жизненного цикла QObject правильно использовать этот инструмент невозможно.
3. Сигналы, слоты и sender()
Метод sender(), возвращающий QObject*, — еще одна причина существования сырых указателей. Попытка обернуть этот результат в умный контейнер — это попытка забрать владение у системы, которая им уже управляет. Это кратчайший путь к падению приложения.
4. QML и неявная передача владения (Ownership)
Если вы используете QML, ситуация с указателями становится еще острее. Когда вы передаете QObject* из C++ в QML (через сигналы, методы или свойства), вступает в силу механизм QQmlEngine::ObjectOwnership.
Ловушка JavaScript-владения
Если объект был создан в C++, но у него нет родителя (parent == nullptr), то при передаче в QML движок JavaScript может решить, что теперь он владеет этим объектом:
JavaScript-сборщик мусора (GC) видит, что объект больше не используется в QML-коде.
GC удаляет объект.
Ваш C++ указатель превращается в «тыкву» (dangling pointer).
Результат: Непредсказуемый краш спустя случайное время после вызова GC.
Чтобы этого избежать, разработчики вынуждены либо всегда назначать родителя, либо явно вызывать QQmlEngine::setObjectOwnership(obj, QQmlEngine::CppOwnership). Умные указатели C++ здесь бессильны, так как они не могут контролировать сборщик мусора JavaScript.
А когда, вообще, стоит выбрать умные указатели от Qt, а когда от std?
Если вам все же нужно использовать умные указатели (например, для объектов без родителя), часто возникает дилемма: std::unique_ptr или QScopedPointer? В контексте Qt-разработки у вторых есть преимущества:
Интеграция с контейнерами Qt: Классы вроде
QListисторически лучше оптимизированы под работу с объектами, управляемыми черезQSharedPointer.Бинарная совместимость (ABI):
Q-аналоги гарантируют стабильность в рамках минорных версий Qt, что важно для разработки библиотек.Кастомизация удаления:
QScopedPointerпозволяет удобно использоватьQScopedPointerDeleteLater, что критично для объектов вродеQNetworkReply, которые нельзя удалять немедленно.
Несмотря на мощь Qt, стандартная библиотека C++ (STL) в некоторых случаях оказывается эффективнее и правильнее:
Производительность:
std::unique_ptrимеет нулевой оверхед (zero-cost abstraction). Он чуть легче, чемQScopedPointer.Стандартные алгоритмы и библиотеки: Если вы используете сторонние библиотеки (Boost, OpenCV), они ожидают
std-указатели.Бизнес-логика (Domain Model): Код, не завязанный на GUI и
QObject, лучше писать на «чистом» C++. Это делает логику переносимой и упрощает юнит-тестирование.Современные возможности C++:
std::shared_ptrподдерживаетstd::make_shared, что позволяет выделить память под объект и счетчик ссылок одним блоком.
Итоговые рекомендации
Чтобы не запутаться в двух системах управления памятью, следуйте этому простому алгоритму:
Создаете
QObjectс родителем? Используйте сырой указатель. Памятью управляет родитель.new QLabel("Text", this)— это норма.Передаете объект в QML? Убедитесь, что у него есть родитель, или явно установите CppOwnership. В QML почти всегда «гуляют» сырые указатели.
Создаете
QObjectБЕЗ родителя? ИспользуйтеQScopedPointer(илиstd::unique_ptr). Это защитит от утечек.Нужно следить за объектом, который может быть удален кем-то другим? Используйте
QPointer. Это единственный безопасный способ проверить, жив ли еще виджет.Объект не наследует
QObject(Бизнес-логика)? Используйтеstd::unique_ptrдля владения иstd::shared_ptrдля разделяемых ресурсов.
Заключение
Программа на Qt — это баланс между классическим RAII и иерархическим владением. Попытка использовать только умные указатели — это борьба с фреймворком.
Комментарии (5)

andy_p
22.04.2026 04:25Все эти умные указатели возникли из-за плохо продуманной архитектуры приложения, когда непонятно, кто владелец объекта, кто кого создает и удаляет. В Qt с архитектурой всё в порядке, поэтому такой проблемы не возникает.

cyanidle
22.04.2026 04:25Какая то ужасно дилетантская статья. Скорее всего сгенерирована нейросетью по запросу состоящему из заголовка.
1)QScopedPointer<QLabel> label(newQLabel("Hello", parent));
Дабл фри здесь не будет, в деструкторе ребенек убирается себя из списка детей. Зачем здесь ScopedPointer тоже непонятно, можно просто создать объект на стэке, если он будет нужен лишь временно - результат будет тот же (так иногда делают с QMsgBox). Ногострел может быть если только в рамках этого скоупа умрет уже сам родитель.
2)Любой умный указатель в таком цикле создаст оверхед.
Умный указатель в цикле просто не нужен, вы получаете невладеющий указатель на сырые байты изображения. Зачем тут вообще указатели если владеет данными сам QImage?
3)Хотя в новых версиях Qt иногда применяется QScopedPointer, использование std-аналогов там затруднено требованиями к бинарной совместимости (ABI).
Все намешано в кучу, std умные указатели ничем не хуже кутэшных при создании Pimpl. Человек даже не читал выхлоп нейросети, которая написала полную чушь. Pimpl как раз позволяет избежать проблем ABI и неважно как он реализован, эта идиома используется не только в QT
И так продолжать можно почти по каждому пункту, статья - мусор
yamix Автор
22.04.2026 04:25Спасибо за замечания! Действительно не доработал и поспешил выложить. Сейчас уже всё поправил.

Mingun
22.04.2026 04:25Ну да, как же. Сначала говорим про
QScopedPointer, а через пару слов на голубом глазу у насQSharedPointer. Дождитесь, пока нейросеть обучится нормально, а потом статьи строчите.
sergio_nsk
Это наследие компиляторов без шаблонов. Почему не обновят/дополнят API, не понятно. Они, ведь, уже не раз забивали на совместимость.