умные указатели (boost) объяснили

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

Примеры были бы оценены!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

Вы используете boost в производственном коде?

Ответ 1

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

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

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

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

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

Некоторые владеющие интеллектуальными указателями не поддерживают ни вторую, ни третью. Поэтому они не могут быть возвращены из функций или переданы где-то в другом месте. Это наиболее подходит для целей 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 указывает на уже удаленный объект, он заметит вас, исключив исключение. Использование weak_ptr наиболее полезно, когда вы циклическая ссылка: подсчет ссылок не может легко справиться с такой ситуацией.

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

Ответ 2

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

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

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 обычно используется, когда есть сторонний смарт-птр, который вы должны использовать. Он будет вызывать бесплатную функцию для добавления и уменьшения количества ссылок. См. Ссылку для дополнительной документации для получения дополнительной информации.

Ответ 3

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

Ответ 4

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

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

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