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


220

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

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

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

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

Ответы:


339

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

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

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

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

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

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


3
спасибо за похвалу чувак. я ценю это, поэтому вы тоже получите +1 сейчас: p
Йоханнес Шауб - litb

@litb: я сомневаюсь в "передаче права собственности"; Я согласен, что в C ++ 03 нет реальной передачи прав собственности между объектами, но для интеллектуальных указателей это невозможно сделать с помощью механизма деструктивного копирования , указанного здесь. Informit.com/articles/article.aspx?p=31529&seqNum= 5 .
legends2k

3
фантастический ответ. Примечание: auto_ptrуже устарело (C ++ 11).
Николай

2
«Это, в свою очередь, нарушает его использование в контейнерах, поскольку требования определяют определенное поведение конструктора копирования элементов контейнеров, которое несовместимо с так называемым поведением« умных указателей »этих интеллектуальных указателей». Не получил эту часть.
Раджа

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

91

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


разве не if (tPtrAccessed[0].get() == 0)должно быть if (tPtrAccessed.get() == 0) ?
Раджешвар

@DougT. Верите ли вы, что Java использует ту же идею с References? Мягкий, жесткий, слабый и т. Д.?
gansub

20

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


Фактически, в прошлый раз, когда я попробовал это, бенчмаркинг показал, что разрыв в производительности значительно сократился, так как я изначально писал это, по крайней мере, на типичном ПК HW! Однако более эффективный подход ptr_container может иметь некоторые преимущества в нишах.
Timday

12

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

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

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


8
Я пришел сюда, потому что нашел документацию надстройки слишком страшной.
Франсуа Бота
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.