умные указатели (повышение) объяснил - PullRequest
214 голосов
/ 20 февраля 2009

В чем разница между следующим набором указателей? Когда вы используете каждый указатель в производственном коде, если вообще?

Примеры приветствуются!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

Используете ли вы boost в рабочем коде?

Ответы [ 4 ]

332 голосов
/ 20 февраля 2009

Основные свойства умных указателей

Это просто, когда у вас есть свойства, которые вы можете назначить каждому умному указателю. Есть три важных свойства.

  • вообще не владеет
  • передача права собственности
  • доля владения

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

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

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

Доля владения может быть реализована с помощью конструктора копирования. Это естественно копирует умный указатель, и копия, и оригинал будут ссылаться на один и тот же объект. Передача права собственности в настоящее время не может быть реализована в C ++, потому что нет средств для передачи чего-либо из одного объекта в другой, поддерживаемых языком: если вы пытаетесь вернуть объект из функции, происходит то, что объект копируется. Таким образом, умный указатель, который реализует передачу права собственности, должен использовать конструктор копирования для реализации этой передачи права собственности. Однако это, в свою очередь, нарушает его использование в контейнерах, поскольку требования определяют определенное поведение конструктора копирования элементов контейнеров, которое несовместимо с так называемым поведением «движущегося конструктора» этих интеллектуальных указателей.

C ++ 1x обеспечивает встроенную поддержку передачи права собственности, вводя так называемые «конструкторы перемещения» и «операторы назначения перемещения». Он также поставляется с таким интеллектуальным указателем передачи права собственности, который называется unique_ptr.

Категоризация умных указателей

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

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

weak_ptr - не принадлежащий интеллектуальный указатель. Он используется для ссылки на управляемый объект (управляемый shared_ptr) без добавления счетчика ссылок. Обычно вам нужно получить необработанный указатель из shared_ptr и скопировать его. Но это было бы небезопасно, поскольку у вас не было бы способа проверить, когда объект был фактически удален. Таким образом, weak_ptr предоставляет средства, ссылаясь на объект, управляемый shared_ptr. Если вам нужен доступ к объекту, вы можете заблокировать управление им (чтобы избежать того, что в другом потоке shared_ptr освобождает его при использовании объекта), а затем использовать его. Если слабый_птр указывает на уже удаленный объект, он заметит вас, выдав исключение. Использование weak_ptr наиболее полезно, когда у вас есть циклическая ссылка: подсчет ссылок не может легко справиться с такой ситуацией.

intrusive_ptr похож на shared_ptr, но он не сохраняет счетчик ссылок в shared_ptr, но оставляет увеличение / уменьшение счетчика некоторым вспомогательным функциям, которые должны быть определены управляемым объектом. Это имеет преимущество в том, что уже ссылочный объект (который имеет счетчик ссылок, увеличенный с помощью внешнего механизма подсчета ссылок) может быть вставлен в intrusive_ptr - потому что счетчик ссылок больше не является внутренним для интеллектуального указателя, но интеллектуальный указатель использует существующий механизм подсчета ссылок.

unique_ptr - указатель передачи права собственности. Вы не можете скопировать его, но вы можете переместить его с помощью конструкторов перемещения C ++ 1x:

unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!

Это семантика, которой подчиняется std :: auto_ptr, но из-за отсутствия собственной поддержки перемещения он не может обеспечить их без ошибок. unique_ptr автоматически украдет ресурсы из временного другого unique_ptr, который является одной из ключевых особенностей семантики перемещения. auto_ptr будет устаревшим в следующем выпуске C ++ Standard в пользу unique_ptr. C ++ 1x также позволит вставлять объекты, которые являются только подвижными, но не копируемыми в контейнеры. Так что вы можете, например, втиснуть unique_ptr в вектор. Я остановлюсь здесь и отсылаю вас к прекрасной статье об этом, если вы хотите узнать больше об этом.

90 голосов
/ 20 февраля 2009

scoped_ptr является самым простым. Когда он выходит за рамки, он уничтожается. Следующий код недопустим (scoped_ptrs не подлежит копированию), но проиллюстрирует точку:

std::vector< scoped_ptr<T> > tPtrVec;
{
     scoped_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory

shared_ptr - подсчет ссылок. Каждый раз, когда происходит копирование или присвоение, счетчик ссылок увеличивается. Каждый раз, когда запускается деструктор экземпляра, счетчик ссылок для необработанного T * уменьшается. Если значение равно 0, указатель освобождается.

std::vector< shared_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     // This copy to tPtrVec.push_back and ultimately to the vector storage
     // causes the reference count to go from 1->2
     tPtrVec.push_back(tPtr);
     // num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe

слабый_птр - это слабая ссылка на общий указатель, которая требует от вас проверить, находится ли намеченный общий_птр все еще около

std::vector< weak_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed =  tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
     cout << "Raw T* was freed, can't access it"
}
else
{
     tPtrVec[0]->DoSomething(); // raw 
}

intrusive_ptr обычно используется, когда требуется сторонний смарт-ptr. Он вызовет бесплатную функцию для добавления и уменьшения счетчика ссылок. См. Ссылку , чтобы увеличить документацию для получения дополнительной информации.

20 голосов
/ 20 февраля 2009

Не забывайте boost::ptr_container в любом обзоре интеллектуальных указателей повышения. Они могут быть неоценимы в ситуациях, когда, например, std::vector<boost::shared_ptr<T> > будет слишком медленным.

12 голосов
/ 20 февраля 2009

Я второй совет о просмотре документации. Это не так страшно, как кажется. И несколько коротких подсказок:

  • scoped_ptr - указатель автоматически удаляется при выходе из области видимости. Примечание - назначение невозможно, но вводится без накладных расходов
  • intrusive_ptr - указатель подсчета ссылок без издержек smart_ptr. Однако сам объект хранит счетчик ссылок
  • weak_ptr - работает вместе с shared_ptr для разрешения ситуаций, приводящих к круговым зависимостям (прочитайте документацию и поищите красивую картинку в google;)
  • shared_ptr - универсальные, самые мощные (и тяжелые) из умных указателей (из предложенных boost)
  • Существует также старый auto_ptr, который гарантирует, что объект, на который он указывает, автоматически уничтожается, когда управление покидает область действия. Однако у него другая семантика копирования, чем у остальных парней.
  • unique_ptr - будет поставляться с C ++ 0x

Ответ на редактирование: Да

...