Умные указатели (повышение) объяснил

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

Примеры будут оценены!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

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

4 ответа

Решение

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам