В C ++ есть много указателей, но, если честно, через 5 лет или около того в программировании на C ++ (особенно с Qt Framework) я использую только старый необработанный указатель:
SomeKindOfObject *someKindOfObject = new SomeKindOfObject();
Я знаю, что есть много других «умных» указателей:
// shared pointer:
shared_ptr<SomeKindofObject> Object;
// unique pointer:
unique_ptr<SomeKindofObject> Object;
// weak pointer:
weak_ptr<SomeKindofObject> Object;
Но я не имею ни малейшего представления о том, что с ними делать и что они могут мне предложить по сравнению с необработанными указателями.
Например, у меня есть этот заголовок класса:
#ifndef LIBRARY
#define LIBRARY
class LIBRARY
{
public:
// Permanent list that will be updated from time to time where
// each items can be modified everywhere in the code:
QList<ItemThatWillBeUsedEveryWhere*> listOfUselessThings;
private:
// Temporary reader that will read something to put in the list
// and be quickly deleted:
QSettings *_reader;
// A dialog that will show something (just for the sake of example):
QDialog *_dialog;
};
#endif
Это, очевидно, не является исчерпывающим, но для каждого из этих 3-х указателей это нормально, чтобы оставить их «сырые» или я должен использовать что-то более подходящее?
И во второй раз, если работодатель будет читать код, будет ли он строг в отношении того, какие указатели я использую или нет?
c++
programming-practices
pointers
qt
smart-pointer
CheshireChild
источник
источник
Ответы:
«Сырой» указатель неуправляемый. То есть следующая строка:
... утечка памяти, если сопровождающее
delete
не выполнено в надлежащее время.auto_ptr
Чтобы минимизировать эти случаи,
std::auto_ptr<>
был введен. Однако из-за ограничений C ++ до стандарта 2011 годаauto_ptr
утечка памяти все еще очень проста . Это достаточно для ограниченных случаев, таких как это, однако:Один из его самых слабых вариантов использования находится в контейнерах. Это связано с тем, что если
auto_ptr<>
сделана копия объекта, а старая копия не была тщательно сброшена, то контейнер может удалить указатель и потерять данные.unique_ptr
В качестве замены C ++ 11 представил
std::unique_ptr<>
:Такое
unique_ptr<>
будет правильно очищено, даже если оно передается между функциями. Это достигается семантическим представлением «владения» указателя - «владелец» очищает его. Это делает его идеальным для использования в контейнерах:В отличие от этого
auto_ptr<>
,unique_ptr<>
здесь хорошо себя ведут, и приvector
изменении размеров ни один из объектов не будет случайно удален, покаvector
копирует свое резервное хранилище.shared_ptr
а такжеweak_ptr
unique_ptr<>
Конечно, это полезно, но есть случаи, когда вы хотите, чтобы две части вашей кодовой базы могли ссылаться на один и тот же объект и копировать указатель, при этом гарантируя надлежащую очистку. Например, дерево может выглядеть так при использованииstd::shared_ptr<>
:В этом случае мы можем даже удерживать несколько копий корневого узла, и дерево будет должным образом очищено после уничтожения всех копий корневого узла.
Это работает, потому что каждый
shared_ptr<>
держит не только указатель на объект, но также и счетчик ссылок всехshared_ptr<>
объектов, которые ссылаются на один и тот же указатель. Когда создается новый, количество увеличивается. Когда кто-то уничтожен, счет уменьшается. Когда счетчик достигает нуля, указатель равенdelete
d.Таким образом, возникает проблема: структуры с двойными связями заканчиваются циклическими ссылками. Скажем, мы хотим добавить
parent
указатель на наше деревоNode
:Теперь, если мы удалим a
Node
, есть циклическая ссылка на него. Это никогда не будетdelete
d, потому что его счетчик ссылок никогда не будет нулевым.Чтобы решить эту проблему, вы используете
std::weak_ptr<>
:Теперь все будет работать правильно, и удаление узла не оставит застрявших ссылок на родительский узел. Это делает прогулку по дереву немного более сложной, однако:
Таким образом, вы можете заблокировать ссылку на узел, и у вас есть разумная гарантия, что он не исчезнет, пока вы работаете над ним, поскольку вы держитесь
shared_ptr<>
за него.make_shared
а такжеmake_unique
Теперь, есть некоторые незначительные проблемы с
shared_ptr<>
иunique_ptr<>
которые должны быть решены. Следующие две строки имеют проблему:Если
thrower()
выдает исключение, обе строки будут пропускать память. Более того,shared_ptr<>
счетчик ссылок находится далеко от объекта, на который он указывает, и это может означать повторное распределение). Это обычно не желательно.C ++ 11 предоставляет
std::make_shared<>()
и C ++ 14 предоставляетstd::make_unique<>()
решить эту проблему:Теперь в обоих случаях, даже если
thrower()
выдает исключение, не будет утечки памяти. В качестве бонуса,make_shared<>()
имеет возможность создать счетчик ссылок в том же пространстве памяти, что и управляемый объект, который может быть быстрее и сэкономить несколько байтов памяти, обеспечивая при этом исключительную гарантию безопасности!Заметки о Qt
Следует отметить, однако, что Qt, который должен поддерживать компиляторы до C ++ 11, имеет свою собственную модель сборки мусора: у многих
QObject
есть механизм, где они будут уничтожены должным образом без необходимости пользователя дляdelete
них.Я не знаю, как
QObject
будет вести себя s при управлении с помощью управляемых указателей C ++ 11, поэтому не могу сказать, чтоshared_ptr<QDialog>
это хорошая идея. У меня недостаточно опыта работы с Qt, чтобы сказать наверняка, но я считаю, что Qt5 был настроен для этого варианта использования.источник
shared_ptr
это отдельный объект - отдельное выделение - отnew
объекта ed. Они существуют в разных местах.make_shared
имеет возможность объединить их в одном месте, что, помимо прочего, улучшает локальность кэша.shared_ptr
это объект. И чтобы управлять объектом, он должен выделить объект (счетчик ссылок (слабый + сильный) + разрушитель) -объект.make_shared
позволяет выделить этот объект и управляемый объект как единое целое.unique_ptr
не использует их, таким образом, нет никакого соответствующего преимущества, кроме того, чтобы удостовериться, что объект всегда принадлежит смарт-указателю. Кроме того, можно иметь a,shared_ptr
который владеет базовым объектом и представляет anullptr
, или который не является владельцем и представляет ненулевой указатель.shared_ptr
делает: 1. Он разделяет владение каким-либо объектом (представлен внутренним динамически размещаемым объектом, имеющим слабый и сильный счетчик ссылок, а также средство удаления). , 2. Содержит указатель. Эти две части независимы.make_unique
иmake_shared
оба убедитесь, что выделенный объект безопасно помещен в смарт-указатель. Кроме того,make_shared
позволяет выделить объект владения и управляемый указатель вместе.