MyTetra Share
Делитесь знаниями!
В чем основные отличия умных указателей QPointer, QScopedPointer, QSharedPointer
Время создания: 01.11.2024 14:38
Текстовые метки: qt, c++, си++, умный, указатель, QPointer, QScopedPointer, QSharedPointer
Раздел: Компьютер - Программирование - Язык C++ (Си++) - Библиотека Qt - Принципы написания кода
Запись: xintrea/mytetra_syncro/master/base/1730461115jqk4959m4c/text.html на raw.github.com

Умные указатели QPointer, QScopedPointer, QSharedPointer имеют различные тонкости в своей работе. Что бы не забыть в чем они состоят, здесь записываются основные моменты.



Указатель QPointer


Данный указатель способен указывать и работать только с объектом QObject или с наследниками данного класса.


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


По сути, основным свойством, которое используется в указателе QPointer является то, что он способен автоматически выставить свое значение в nullptr в тот момент, когда объект QObject, на который он указывает, был удален.


Пример использования:



// Создание объекта и инициализация указателя QPointer

QPointer<ExampleClass> pointer = new ExampleClass();


// Инициализация дополнительного обычного C++ указателя

// Метод QPointer data() возвращает обычный C++ указатель

ExampleClass *rawPointer = pointer.data();


...


// Удаление объекта, вариант 1

delete pointer.data();


// После удаления объекта, переменная pointer будет равна nullptr

// По сути, это произошло удаление объекта через сырой указатель


...


// Удаление объекта, вариант 2, чтобы показать что удалять объект можно

// через любой указатель, и QPinter узнает об удалении объекта

delete rawPointer;


// После удаления объекта, переменная pointer будет равна nullptr

// Это произойдет потому, что QObject через метаобъектную систему

// оповестит указатели QPointer что он был удален. И поэтому

// даже при удалении через дополнительный "сырой" указатель,

// указатель QPointer узнает что объект был удален и выставит

// свое значение в nullptr



QPointer полезен, когда нужно временно отслеживать объект, переданный в функцию или присвоенный в классе, но нет необходимости напрямую управлять его удалением. Например, если указатель сохраняется только для выполнения операций и может исчезнуть в любое время, QPointer предотвращает доступ к невалидным объектам. По сути, применять его следует когда объект создается и удаляется где-то ещё, а программист только хочет иметь безопасный указатель на него в другом месте. Это позволяет избежать ситуации, когда удаление объекта в одной части программы вызывает неопределённое поведение в другой части.



QPointer<Worker> worker = new Worker();


...

// Где-то происходит удаление

delete worker; // Удаляем объект

...


if (worker) {

worker->doWork();

} else {

qDebug() << "Объект больше не существует!";

}



Под словом "безопасный" подразумевается только то, что QPointer-указатель на объект всегда станет равным nullptr каким бы способом этот объект не уничтожался (напрямую, через дополнительный указатель, при удалении массива указателей, куда он был положен и т.д.). Саму проверку на существование (на равенство nullptr) никто не отменял! Нельзя просто так взять и вызвать метод у удаленного объекта, ибо будет сегфолт.



И еще одно важное свойство QPointer: когда объект, на который указывает QPointer, удаляется, Qt автоматически удаляет все соединения с его сигналами/слотами. Это, как минимум, предотвращает отправку сигналов на несуществующий объект.



Указатель QScopedPointer


Указатель QScopedPointer, так же как и QPointer, не поддерживает совместное владение объектом.


Указатель QScopedPointer способен указывать не только на QObject, но и на любой другой тип.


Основное предназначение QScopedPointer состоит в том, что он удаляет объект, на который он ссылается, при выходе из текущего скоупа (другими словами - из области видимости). Это делает его полезным для использования с временными объектами или локальными данными, которые нужно освободить автоматически.


Скоуп - это тот текущий блок кода, который ограничен фигурными скобками.


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


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


Пример использования:



#include <QScopedPointer>


void myFunction() {

QScopedPointer<MyClass> ptr(new MyClass);

// Работа с объектом через ptr

ptr->doSomething();


// Автоматическое освобождение памяти при выходе из функции

}



А вот как можно установить указатель не в момент инициализации, а в любом месте кода:



QScopedPointer<MyClass> myPointer;

...

myPointer.reset( new MyClass() ); // Установка указателя на созданный объект



QScopedPointer поддерживает пользовательский метод удаления (deleter), что полезно для объектов, которым нужно освободить ресурсы особым образом.



Указатель QSharedPointer


QSharedPointer — это умный указатель Qt, обеспечивающий автоматическое управление временем жизни объекта через механизм подсчета ссылок. Основные особенности QSharedPointer включают:



Подсчет ссылок и автоматическое удаление:


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



Совместное владение:


Несколько QSharedPointer могут владеть одним и тем же объектом, безопасно управляя его временем жизни. Это полезно, когда объект должен быть доступен из нескольких мест, и необходимо убедиться, что он не будет удален, пока хотя бы один указатель ссылается на него.



Потокобезопасность:


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



Слабые ссылки через QWeakPointer:


QWeakPointer может использоваться вместе с QSharedPointer, чтобы создать слабую ссылку на объект, не влияющую на его время жизни. Это полезно для предотвращения циклических зависимостей и утечек памяти.



Функции create() и makeShared():


QSharedPointer предоставляет функцию QSharedPointer<T>::create(), которая создает и инициализирует объект прямо внутри умного указателя, избегая возможности создания нескольких QSharedPointer для одного объекта и ошибочного подсчета ссылок.



Использование с кастами указателей:


QSharedPointer поддерживает различные виды кастов (dynamicCast, staticCast, constCast), что упрощает работу с полиморфными объектами и позволяет выполнять безопасные преобразования.



Пример использования QSharedPointer



#include <QSharedPointer>

#include <QDebug>


class ExampleClass {

public:

ExampleClass() { qDebug() << "ExampleClass создан"; }

~ExampleClass() { qDebug() << "ExampleClass удалён"; }

};


void exampleFunction() {

QSharedPointer<ExampleClass> ptr1 = QSharedPointer<ExampleClass>::create();

{

QSharedPointer<ExampleClass> ptr2 = ptr1;

qDebug() << "Ссылки на объект:" << ptr1.useCount(); // Выведет: Ссылки на объект: 2

} // ptr2 выходит из области видимости, счетчик ссылок уменьшается

qDebug() << "Ссылки на объект:" << ptr1.useCount(); // Выведет: Ссылки на объект: 1

} // ptr1 выходит из области видимости, объект удаляется



Когда использовать QSharedPointer:


  • Если объект должен существовать в программе до тех пор, пока есть хотя бы одна ссылка на него.
  • В многопоточных средах, где необходима потокобезопасность при работе с указателями.
  • Для безопасного управления объектами в ситуациях, когда несколько классов или функций могут совместно использовать и удалять один и тот же объект.
  • Когда требуется слабая ссылка на объект, чтобы избежать циклических зависимостей.


Так же в этом разделе:
 
MyTetra Share v.0.65
Яндекс индекс цитирования