Вопрос по c++, c++11 – Что такое умный указатель и когда я должен его использовать?

1581

Что такое умный указатель и когда мне его использовать?

Проверьте этот вопрос: <br> Умные указатели: или кто владеет тобой, детка Martin York
Обратите внимание, что реализация std :: auto_ptr в Visual Studio 2005 ужасно нарушена. <br>http: //connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx FeedbackID = 98871 <br>http: //connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx FeedbackID = 101842 Вместо этого используйте усиленные. Richard
Две прекрасные статьи на эту тему: - Умные указатели - что, почему, что? - Гуру недели # 25 Lazer
Я просто хочу сказать, что этов точк вопрос, который я люблю видеть. Хотелось бы, чтобы больше вопросов было таким широким. Seph Reed
Вот (бесплатно) глава Александреску о том, как создавать умные указатели разных вкусов: Informit.com / статьи / article.aspx? Р = 31529 В своей реализации он использует аргументы шаблона в качестве «политик», чтобы указать, какие атрибуты он хочет (например, подсчет ссылок), тогда как в стандартной библиотеке используются отдельные классы. Обратите внимание, что он также писал до того, как стали доступны ссылки на rvalue, чтобы сделать возможным что-то вроде std :: unique_ptr. metal

Ваш Ответ

14   ответов
221

Что такое умный указатель?
Это тип, значения которого можно использовать как указатели, но который предоставляет дополнительную функцию автоматического управления памятью: когда интеллектуальный указатель больше не используется, память, на которую он указывает, освобождается (см. Также более подробное определение в Википедии). Когда я должен использовать один?
В коде, который включает отслеживание владения частью памяти, выделение или удаление; умный указатель часто избавляет вас от необходимости делать это явно. Но какой умный указатель мне использовать в каких случаях? Используйтеstd::unique_ptr когда вы не собираетесь хранить несколько ссылок на один и тот же объект. Например, используйте его для указателя на память, которая выделяется при входе в некоторую область и освобождается при выходе из области. Используйтеstd::shared_ptr когда вы хотите обратиться к своему объекту из нескольких мест - и не хотите, чтобы ваш объект был перераспределен, пока все эти ссылки сами не исчезнут. Используйтеstd::weak_ptr когда вы хотите ссылаться на свой объект из нескольких мест - для тех ссылок, для которых нормально игнорировать и освобождать (поэтому они просто заметят, что объект исчез, когда вы попытаетесь разыменовать). Не используйтеboost:: умные указатели илиstd::auto_ptr за исключением особых случаев, о которых вы можете прочитать, если нужно. Эй, я не спросил, какой использовать!
А, но ты действительно хотел это признать. Так когда же мне использовать обычные указатели?
В основном в коде, который не обращает внимания на владение памятью. Обычно это происходит в функциях, которые получают указатель из другого места и не выделяют или не выделяют, а не хранят копию указателя, которая превышает их выполнение.
Стоит отметить, что хотя умные (владеющие) указатели помогают в правильном управлении памятью, необработанные (не владеющие) указатели по-прежнему полезны для других организационных целей в структурах данных. Херб Саттер дал отличную презентацию по этому вопросу на CppCon 2016, которую вы можете увидеть на YouTube: Leak-Freedom в C ++ ... По умолчанию. wiktor.wandachowicz
Первый действительно простой и понятный ответ, который я нашел в Интернете! Благодарность Bernd
1750

который оборачивает «сырой» (или «голый») указатель C ++ для управления временем жизни объекта, на который указывает указатель. Не существует единого типа интеллектуального указателя, но все они пытаются абстрагировать необработанный указатель практическим способом.

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

С необработанными указателями программист должен явно уничтожить объект, когда он больше не нужен.

// Need to create the object to achieve some goal
MyObject* ptr = new MyObject(); 
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?

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

SomeSmartPtr<MyObject> ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.

// Destruction of the object happens, depending 
// on the policy the smart pointer class uses.

// Destruction would happen even if DoSomething() 
// raises an exception

Простейшая используемая политика включает в себя область действия объекта-оболочки интеллектуального указателя, например, реализованную с помощьюboost::scoped_ptr илиstd::unique_ptr.

void f()
{
    {
       boost::scoped_ptr<MyObject> ptr(new MyObject());
       ptr->DoSomethingUseful();
    } // boost::scopted_ptr goes out of scope -- 
      // the MyObject is automatically destroyed.

    // ptr->Oops(); // Compile error: "ptr" not defined
                    // since it is no longer in scope.
}

Обратите внимание, чтоscoped_ptr экземпляры не могут быть скопированы. Это предотвращает многократное удаление указателя (неправильно). Однако вы можете передавать ссылки на него другим функциям, которые вы вызываете.

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

Более сложная политика интеллектуальных указателей включает подсчет ссылок в указателе. Это позволяет копировать указатель. Когда последняя «ссылка» на объект уничтожается, объект удаляется. Эта политика реализуетсяboost::shared_ptr а такжеstd::shared_ptr.

void f()
{
    typedef std::shared_ptr<MyObject> MyObjectPtr; // nice short alias
    MyObjectPtr p1; // Empty

    {
        MyObjectPtr p2(new MyObject());
        // There is now one "reference" to the created object
        p1 = p2; // Copy the pointer.
        // There are now two references to the object.
    } // p2 is destroyed, leaving one reference to the object.
} // p1 is destroyed, leaving a reference count of zero. 
  // The object is deleted.

казатели с подсчетом ссылок очень полезны, когда время жизни вашего объекта намного сложнее и не привязано напрямую к определенному разделу кода или другому объекту.

У указателей с подсчетом ссылок есть один недостаток - возможность создания свисающей ссылки:

// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!

Другая возможность - создавать круговые ссылки:

struct Owner {
   boost::shared_ptr<Owner> other;
};

boost::shared_ptr<Owner> p1 (new Owner());
boost::shared_ptr<Owner> p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1

// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!

Чтобы обойти эту проблему, Boost и C ++ 11 определилиweak_ptr для определения слабой (неучтенной) ссылки наshared_ptr.

ОБНОВИТ

Этот ответ довольно старый и поэтому описывает то, что было «хорошо» в то время, то есть умные указатели, предоставляемые библиотекой Boost. Начиная с C ++ 11, стандартная библиотека предоставляла достаточно умных указателей типов, поэтому вы должны отдавать предпочтение использованиюstd::unique_ptr, std::shared_ptr а такжеstd::weak_ptr.

Существует такжеstd::auto_ptr. Он очень похож на указатель с ограничением, за исключением того, что он также обладает «особой» опасной способностью копироваться, что также неожиданно передает право собственности! Это устарело в новейших стандартах, поэтому не стоит его использовать. Использоватьstd::unique_ptr вместо.

std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership. 
                                 // p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.
Ты имеешь ввидуstd::auto_ptr<MyObject> p1 (new MyObject()); вместо тогоstd::auto_ptr<MyObject> p1 (new Owner());? Mateen Ulhaq
Отличный ответ. Было бы хорошо, если бы он был обновлен для C ++ 11. Я нашел этот ответ в поисках информации о новом стандарте 11, и было бы неплохо, если бы будущие посетители могли найти обновленную информацию. Я знаю, что auto_ptr устарел. Я полагаю, что shated_ptr и weak_ptr существуют, как описано, и я думаю, что scoped_ptr теперь является уникальным_ptr в стандарте. Если это правда, можно ли обновить этот ответ, пожалуйста? SaulBack
Хороший ответ. Было бы хорошо, если бы он был обновлен для C ++ 14. Эй, знаешь, кто-то сделал такой же комментарий о C ++ 11, более трех лет назад ...: - einpoklum
Сказать, что возможность создания свисающей ссылки является недостатком указателей с подсчетом ссылок, абсолютно безумно. Возможные висячие ссылки являются недостатком любой указатель C ++. На самом деле это именно этот недостаток какие умные указатели предназначены для Облегчить. Michael Dorst
Если вы объявляете указатель на интеллектуальный указатель (как это было сделано в примере), вы сознательно отказываетесь от всех преимуществ интеллектуального указателя. Это не недостаток или недостаток дизайна, это самое идиотское использование, которое только можно себе представить. Michael Dorst
16

который действует как указатель, но дополнительно обеспечивает контроль над созданием, уничтожением, копированием, перемещением и разыменованием.

One может реализовать собственный интеллектуальный указатель, но многие библиотеки также предоставляют реализации интеллектуальных указателей, каждая из которых имеет свои преимущества и недостатки.

Например,Увеличени предоставляет следующие реализации интеллектуальных указателей:

shared_ptr<T> - это указатель наT используя счетчик ссылок, чтобы определить, когда объект больше не нужен.scoped_ptr<T> - указатель, автоматически удаляемый, когда он выходит из области видимости. Назначение невозможно.intrusive_ptr<T> - еще один указатель подсчета ссылок. Это обеспечивает лучшую производительность, чемshared_ptr, но требует типT для обеспечения собственного механизма подсчета ссылок.weak_ptr<T> - слабый указатель, работает вместе сshared_ptr чтобы избежать циклических ссылок.shared_array<T> какshared_ptr, но для массивовT.scoped_array<T> какscoped_ptr, но для массивовT.

Это только одно линейное описание каждого из них и может использоваться по мере необходимости, для получения дополнительной информации и примеров можно посмотреть документацию Boost.

Кроме того, стандартная библиотека C ++ предоставляет три умных указателя;std::unique_ptr для уникального владельца,std::shared_ptr для совместного владения иstd::weak_ptr. std::auto_ptr существовал в C ++ 03, но теперь устарел.

Пожалуйста, объясните, почемуscoped_ptr не похож на локально объявленныйconst unique_ptr - который также удаляется при выходе из области видимости. einpoklum
10

http: //sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.htm

Умный указатель - это объект, который действует, выглядит и ощущается как обычный указатель, но предлагает больше функциональности. В C ++ интеллектуальные указатели реализованы в виде шаблонных классов, которые инкапсулируют указатель и переопределяют стандартные операторы указателя. У них есть ряд преимуществ перед обычными указателями. Они гарантированно инициализируются как нулевые указатели или указатели на объект кучи. Направление через нулевой указатель проверяется. Удаление никогда не требуется. Объекты автоматически освобождаются, когда последний указатель на них исчезает. Одна значительная проблема с этими умными указателями состоит в том, что в отличие от обычных указателей, они не уважают наследование. Умные указатели непривлекательны для полиморфного кода. Ниже приведен пример реализации умных указателей.

Пример

template <class X>
class smart_pointer
{
          public:
               smart_pointer();                          // makes a null pointer
               smart_pointer(const X& x)            // makes pointer to copy of x

               X& operator *( );
               const X& operator*( ) const;
               X* operator->() const;

               smart_pointer(const smart_pointer <X> &);
               const smart_pointer <X> & operator =(const smart_pointer<X>&);
               ~smart_pointer();
          private:
               //...
};

Этот класс реализует умный указатель на объект типа X. Сам объект находится в куче. Вот как это можно использовать:

smart_pointer <employee> p= employee("Harris",1333);

Как и другие перегруженные операторы, p будет вести себя как обычный указатель,

cout<<*p;
p->raise_salary(0.5);
8

http: //en.wikipedia.org/wiki/Smart_pointe

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

103

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

Небольшое вступление доступно на странице Умные указатели - что, почему, что?.

Один из простых типов умных указателей -std::auto_ptr (глава 20.4.5 стандарта C ++), которая позволяет автоматически освобождать память, когда она выходит из области видимости, и является более надежной, чем простое использование указателя при возникновении исключений, хотя и менее гибкая.

Еще один удобный типboost::shared_ptr, который реализует подсчет ссылок и автоматически освобождает память, когда не осталось ссылок на объект. Это помогает избежать утечек памяти и прост в использовании для реализации RAII.

Тема подробно рассматривается в книге «Шаблоны C ++: Полное руководство» Дэвида Вандевурда, Николая М. Хосуттиса, глава Глава 20. Умные указатели. Некоторые темы:

Защита от исключений Холдерс, (примечание,std :: auto_ptr является реализацией такого типа умного указателя) Ресурс Приобретение Инициализация (Это часто используется для безопасного управления ресурсами в C + Ограничения Холдера Подсчет ссылок Счетчик одновременного доступа Разрушение и удаление
+ 1 за то, что он единственный ответ в этом списке (из 18 активных ответов), который даже не упомянул RAII. WhozCraig
38

предоставленные Крисом, Сергдевом и Ллёдом, верны. Я предпочитаю более простое определение, просто чтобы сохранить мою жизнь простой: умный указатель - это просто класс, который перегружает-> а также* операторы. Это означает, что ваш объект семантически выглядит как указатель, но вы можете сделать так, чтобы он делал более крутые вещи, включая подсчет ссылок, автоматическое уничтожение и т. Д.shared_ptr а такжеauto_ptr в большинстве случаев достаточно, но они сопровождаются собственным набором небольших идиосинкразий.

28

например «char *», за исключением того, что когда указатель выходит из области видимости, то то, на что он указывает, также удаляется. Вы можете использовать его, как обычный указатель, используя «->», но не тогда, когда вам нужен фактический указатель на данные. Для этого вы можете использовать "& * ptr".

Это полезно для:

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

Данные членов классов, так что при удалении объекта удаляются также все принадлежащие данные, без какого-либо специального кода в деструкторе (вам нужно быть уверенным, что деструктор является виртуальным, что почти всегда полезно делать ).

Вы можетен хотите использовать умный указатель, когда:

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

Смотрите также

вывоз мусор. Это вопрос переполнения стека относительно владения данными
16

Это очень удобно, потому что вам больше не нужно думать об утилизации предметов вручную.

Самыми распространенными умными указателями являютсяstd::tr1::shared_ptr (илиboost::shared_ptr) и режеstd::auto_ptr. Я рекомендую регулярное использованиеshared_ptr.

shared_ptr очень разносторонний и имеет дело с большим разнообразием сценариев удаления, включая случаи, когда объекты должны быть «переданы через границы DLL» (обычный кошмарный случай, если разныеlibcs используются между вашим кодом и DLL).

2

где вам не нужно беспокоиться о выделении памяти, распределении ресурсов и передаче.

Вы можете очень хорошо использовать эти указатели аналогично тому, как любое распределение работает в Java. В java сборщик мусора делает то же самое, в то время как в Smart Pointers это делается деструкторами.

5

на 3 типа:

1)Raw указатели :

T a;  
T * _ptr = &a; 

Они держат в памяти адрес места в памяти. Используйте с осторожностью, так как программы становятся сложными для отслеживания.

Указатели с постоянными данными или адресом {Читать в обратном направлении}

T a ; 
const T * ptr1 = &a ; 
T const * ptr1 = &a ;

Указатель на тип данных T, который является константой. Это означает, что вы не можете изменить тип данных с помощью указателя. то есть*ptr1 = 19; не будет работать. Но вы можете перемещать указатель. то естьptr1++ , ptr1--; и т. д. будет работать. Читать в обратном направлении: указатель на тип T, который является константой

  T * const ptr2 ;

Постоянный указатель на тип данных T. Это означает, что вы не можете перемещать указатель, но вы можете изменить значение, на которое указывает указатель. то есть*ptr2 = 19 будет работать ноptr2++ ; ptr2-- и т. д. не будет работать. Читайте задом наперед: постоянный указатель на тип T

const T * const ptr3 ; 

Постоянный указатель на постоянный тип данных T. Это означает, что вы не можете ни переместить указатель, ни изменить указатель типа данных на указатель. то естьptr3-- ; ptr3++ ; *ptr3 = 19; не будет работат

3) Умные Указатели: #include <memory> }

Shared Pointer:

  T a ; 
     //shared_ptr<T> shptr(new T) ; not recommended but works 
     shared_ptr<T> shptr = make_shared<T>(); // faster + exception safe

     std::cout << shptr.use_count() ; // 1 //  gives the number of " 
things " pointing to it. 
     T * temp = shptr.get(); // gives a pointer to object

     // shared_pointer used like a regular pointer to call member functions
      shptr->memFn();
     (*shptr).memFn(); 

    //
     shptr.reset() ; // frees the object pointed to be the ptr 
     shptr = nullptr ; // frees the object 
     shptr = make_shared<T>() ; // frees the original object and points to new object

Реализовано использование подсчета ссылок для отслеживания количества «вещей», указывающих на объект, на который указывает указатель. Когда этот счетчик становится равным 0, объект автоматически удаляется, то есть объект, на который возражают, удаляется, когда все share_ptr, указывающие на объект, выходят из области видимости. Это избавляет от головной боли, связанной с необходимостью удалять объекты, выделенные вами с помощью new.

Слабый указатель: Помогает справиться с циклической ссылкой, которая возникает при использовании общего указателя. Если у вас есть два объекта, на которые указывают два общих указателя, и есть внутренний общий указатель, указывающий на общий указатель каждого другого, тогда будет циклическая ссылка, и объект не будет удален когда общие указатели выходят из области видимости. Чтобы решить эту проблему, измените внутренний член с shared_ptr на weak_ptr. Примечание. Чтобы получить доступ к элементу, на который указывает слабый указатель, используйте lock (), это возвращает слабый_прит.

T a ; 
shared_ptr<T> shr = make_shared<T>() ; 
weak_ptr<T> wk = shr ; // initialize a weak_ptr from a shared_ptr 
wk.lock()->memFn() ; // use lock to get a shared_ptr 
//   ^^^ Can lead to exception if the shared ptr has gone out of scope
if(!wk.expired()) wk.lock()->memFn() ;
// Check if shared ptr has gone out of scope before access

Видеть : Когда полезен std :: weak_ptr?

Уникальный указатель: Легкий вес умный указатель с эксклюзивной собственностью. Используется, когда указатель указывает на уникальные объекты, не разделяя объекты между указателями.

unique_ptr<T> uptr(new T);
uptr->memFn(); 

//T * ptr = uptr.release(); // uptr becomes null and object is pointed to by ptr
uptr.reset() ; // deletes the object pointed to by uptr 

Чтобы изменить объект, на который указывает уникальный ptr, используйте семантику перемещения

unique_ptr<T> uptr1(new T);
unique_ptr<T> uptr2(new T);
uptr2 = std::move(uptr1); 
// object pointed by uptr2 is deleted and 
// object pointed by uptr1 is pointed to by uptr2
// uptr1 becomes null 

Ссылки: по сути, они могут быть как константные указатели, то есть указатели, которые являются константными и не могут быть перемещены с лучшим синтаксисом.

Видеть : Каковы различия между переменной-указателем и ссылочной переменной в C ++?

r-value reference : reference to a temporary object   
l-value reference : reference to an object whose address can be obtained
const reference : reference to a data type which is const and cannot be modified 

Ссылка :https: //www.youtube.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_n Спасибо Андре за указание на этот вопрос.

3

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

Автоматический указатель, хотя и выглядит похожим, полностью отличается от интеллектуального указателя. Это удобный класс, который освобождает ресурс всякий раз, когда объект автоматического указателя выходит из области видимости переменной. В некоторой степени это делает указатель (на динамически выделяемую память) похожим на переменную стека (статически выделяется во время компиляции).

1

но не охватывают, что делать, если умный указатель не является (полным) ответом на проблему, которую вы пытаетесь решить.

Среди других вещей (хорошо объясненных в других ответах) использование умного указателя является возможным решением Как мы используем абстрактный класс в качестве возвращаемого типа функции? который был отмечен как дубликат этого вопроса. Тем не менее, первый вопрос, который нужно задать, если возникает желание указать абстрактный (или фактически любой) базовый класс в качестве возвращаемого типа в C ++, это «что вы на самом деле имеете в виду?». Существует хорошее обсуждение (с дальнейшими ссылками) идиоматического объектно-ориентированного программирования на C ++ (и как это отличается от других языков) в документации библиотека контейнеров указателей. Таким образом, в C ++ вы должны думать о владении. Какие умные указатели помогают вам, но не являются единственным решением или всегда полным решением (они не дают вам полиморфную копию) и не всегда являются решением, которое вы хотите показать в своем интерфейсе (а возвращаемая функция звучит ужасно очень похоже на интерфейс). Например, может быть достаточно вернуть ссылку. Но во всех этих случаях (умный указатель, контейнер указателя или просто возвращение ссылки) вы изменили возврат сценност в какой-то формессылк. Если вам действительно нужна копия, вам может потребоваться добавить больше шаблонной «идиомы» или перейти от идиоматического (или иного) ООП в C ++ к более общему полиморфизму с использованием таких библиотек, как Adobe Poly или Boost.TypeErasure.

1

умный указатель std :: shared_ptr не имеет оператора подписки и не поддерживает арифметику понтеров, мы можем использовать get () для получения встроенного указателя.

Это было добавлено в C ++ 17, но гарантированно не будет работать ни для чего, кроме простых массивов. HolyBlackCat
@ HolyBlackCat, спасибо за информацию suresh m
Это не отвечает на вопрос. Пожалуйста, рассмотрите возможность удаления своего ответа (и, возможно, сделав его комментарием.) einpoklum

Похожие вопросы