Все дело в собственности. Считается, что сущность (функция или объект) «владеет» указателем, если это задача этой сущности, чтобы гарантировать, что указатель удален. Каждый раз, когда вы используете new
, кто-то, где-то должен стать владельцем возвращаемого указателя. Если это не так, у вас утечка памяти.
Целью всех видов умных указателей является моделирование определенной формы собственности. Деструктор умного указателя - это то, что может вызвать удаление указателя.
std::auto_ptr
(до тех пор, пока вы его не скопируете) моделирует единоличное владение. То есть, любой объект, имеющий экземпляр auto_ptr
, является объектом, который вызовет уничтожение этого указателя. Благодаря хакерству, копирование auto_ptr
фактически передает право собственности с копируемого объекта на объект, который копируется (примечание: никогда не делайте этого).
Итак, если у вас есть это:
std::auto_ptr<int> p1 = new int(4);
Это гарантированно будет уничтожено (при условии, что лицо, его удерживающее, должным образом очищено). Когда p1
падает со стека, указатель будет уничтожен. Если p1
был членом класса, указатель будет уничтожен при уничтожении этого экземпляра класса. Время жизни указателя ограничено. Boost фактически имеет не копируемый эквивалент, который называется boost::scoped_ptr
.
Есть несколько правил, которые вы должны соблюдать при использовании любых интеллектуальных указателей. Это самое важное.
Правило № 1: Если вы создаете экземпляр умного указателя из голого указателя, то вы говорите: «Ни один объект в настоящее время не владеет этим указателем. Теперь я давая вам право собственности , умный указатель. " Вот что значит построить объект умного указателя из голого указателя.
Этот код является нарушением Правила № 1:
std::auto_ptr<int> p1 = new int(4);
std::auto_ptr<int> p2 = p1.get();
Функция auto_ptr::get()
возвращает указатель, так что это допустимый код C ++ (компилируется). Однако и p1
, и p2
теперь считают, что они владеют указателем. Потому что auto_ptr
только для моделей с одним -обладанием, что недопустимо. Когда p2
уничтожен, он удалит указатель. Затем, когда p1
будет уничтожен, он попытается удалить такой же указатель.
К сожалению.
Теперь, когда мы все ясно понимаем правило № 1, давайте посмотрим на shared_ptr
. Этот умный указатель моделирует общих владельцев. Несколько объектов могут одновременно претендовать на владение указателем. Указатель будет удален только после того, как все из них закончат его использование. Таким образом, если все 3 объекта содержат shared_ptr
к одному и тому же объекту, этот объект не будет удален, пока не будут удалены все три, которые содержат shared_ptr
.
Важно понимать, что shared_ptr
- это умный указатель. И, следовательно, оно подпадает под правило № 1.
Это может не иметь смысла. В конце концов, это должно разрешить совместное владение. Это должно означать, что это допустимо, верно?
shared_ptr<int> p1 = new int(4);
shared_ptr<int> p2 = p1.get();
Неа. Это все еще не так. Разница между auto_ptr
и shared_ptr
заключается в том, что вы можете сделать это с помощью shared_ptr
:
shared_ptr<int> p1 = new int(4);
shared_ptr<int> p2 = p1;
Это копия конструкции p2
из p1
. Существует принципиальная разница между созданием shared_ptr
из голого указателя и созданием из уже существующего shared_ptr
. Последнее - то, что фактически разделяет собственность между этими двумя. Первый просто вызовет зло.
Поэтому, если у вас когда-нибудь есть голый указатель и вы положите его в shared_ptr
, вы можете сделать это только для этого указателя один раз. Если вы хотите поделиться правами собственности, вы должны скопировать его вокруг .
Существует один полу-бэкдор: enable_shared_from_this
. Если вы наследуете класс от этого типа, то у вашего класса будет shared_from_this
закрытый член, который функции-члены класса могут использовать для передачи прав собственности. Итак:
struct Type : public enable_shared_from_this
{
DoSomething()
{
shared_ptr<Type> p2 = shared_from_this();
FunctionThatTakesOwnership(p2);
}
};
shared_ptr<Type> p1 = new Type;
p1->DoSomething();
Кроме этого, вам придется передать право собственности, явно скопировав объект.