Что такое умный указатель и когда я должен его использовать?
Что такое умный указатель и когда я должен его использовать?
Ответы:
ОБНОВИТЬ
Этот ответ довольно старый и поэтому описывает то, что было «хорошо» в то время, то есть умные указатели, предоставляемые библиотекой Boost. Начиная с C ++ 11, стандартная библиотека предоставляла достаточное количество типов интеллектуальных указателей, поэтому вы должны отдавать предпочтение использованию std::unique_ptr
, std::shared_ptr
и std::weak_ptr
.
Был также std::auto_ptr
. Это было очень похоже на указатель в области видимости, за исключением того, что он также имел «особую» опасную способность для копирования, которая также неожиданно передает право собственности.
Он был объявлен устаревшим в C ++ 11 и удален в C ++ 17 , поэтому его не следует использовать.
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.
СТАРЫЙ ОТВЕТ
Интеллектуальный указатель - это класс, который оборачивает «сырой» (или «голый») указатель C ++ для управления временем жизни объекта, на который указывает указатель. Не существует единого интеллектуального типа указателя, но все они пытаются абстрагировать необработанный указатель практическим способом.
Умные указатели должны быть предпочтительнее сырых указателей. Если вы чувствуете, что вам нужно использовать указатели (сначала подумайте, действительно ли вы это делаете), вы, как правило, захотите использовать умный указатель, поскольку это может облегчить многие проблемы с необработанными указателями, в основном забывая удалить объект и потеряв память.
С необработанными указателями программист должен явно уничтожить объект, когда он больше не нужен.
// 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()
{
{
std::unique_ptr<MyObject> ptr(new MyObject());
ptr->DoSomethingUseful();
} // ptr goes out of scope --
// the MyObject is automatically destroyed.
// ptr->Oops(); // Compile error: "ptr" not defined
// since it is no longer in scope.
}
Обратите внимание, что std::unique_ptr
экземпляры не могут быть скопированы. Это предотвращает многократное удаление указателя (неправильно). Однако вы можете передавать ссылки на него другим функциям, которые вы вызываете.
std::unique_ptr
Они полезны, когда вы хотите связать время жизни объекта с конкретным блоком кода или, если вы внедрили его как данные члена в другой объект, время жизни этого другого объекта. Объект существует до тех пор, пока не будет завершен содержащий блок кода или пока сам содержащий объект не будет уничтожен.
Более сложная политика интеллектуальных указателей включает подсчет ссылок указателя. Это позволяет копировать указатель. Когда последняя «ссылка» на объект уничтожается, объект удаляется. Эта политика реализуется 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 {
std::shared_ptr<Owner> other;
};
std::shared_ptr<Owner> p1 (new Owner());
std::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 определили a weak_ptr
для определения слабой (неучтенной) ссылки на a shared_ptr
.
std::auto_ptr<MyObject> p1 (new MyObject());
вместо std::auto_ptr<MyObject> p1 (new Owner());
?
const std::auto_ptr
безопасно использовать, если вы застряли с C ++ 03. Я довольно часто использовал его для паттернов pimpl, пока не получил доступ к C ++ 11.
Вот простой ответ для современных C ++ (C ++ 11 и более поздних версий):
std::unique_ptr
когда вы не собираетесь хранить несколько ссылок на один и тот же объект. Например, используйте его для указателя на память, которая выделяется при входе в какую-либо область и освобождается при выходе из области.std::shared_ptr
если вы хотите ссылаться на ваш объект из нескольких мест - и не хотите, чтобы ваш объект был перераспределен, пока все эти ссылки сами не исчезнут.std::weak_ptr
если вы хотите сослаться на ваш объект из нескольких мест - для тех ссылок, для которых нормально игнорировать и освобождать (поэтому они просто заметят, что объект пропал при попытке разыменования).boost::
умные указатели или std::auto_ptr
за исключением особых случаев, о которых вы можете прочитать, если нужно.T*
к std::unique_ptr<T>
тому, что std::weak_ptr<T>
кstd::shared_ptr<T>
Интеллектуальный указатель - это тип, похожий на указатель, с некоторыми дополнительными функциями, такими как автоматическое освобождение памяти, подсчет ссылок и т. Д.
Небольшое вступление доступно на странице Smart Pointers - Что, Почему, Что? ,
Одним из простых типов интеллектуальных указателей является std::auto_ptr
(глава 20.4.5 стандарта C ++), который позволяет автоматически освобождать память, когда она выходит из области видимости, и является более надежным, чем использование простого указателя, когда создаются исключения, хотя и менее гибким.
Другой удобный тип - boost::shared_ptr
это подсчет ссылок, который автоматически освобождает память, когда не осталось ссылок на объект. Это помогает избежать утечек памяти и прост в использовании для реализации RAII .
Тема подробно освещена в книге Дэвида Вандевурде "Николай М. Йосуттис" , глава 20 "Умные указатели " "Шаблоны C ++: полное руководство" . Некоторые темы:
std::auto_ptr
устарело и крайне нежелательно, так как вы можете случайно передать право собственности. - C ++ 11 снимает необходимость Boost, использования: std::unique_ptr
, std::shared_ptr
иstd::weak_ptr
Определения, предоставленные Крисом, Сергдевом и Ллёдом, верны. Я предпочитаю более простое определение , хотя, только чтобы сохранить свою жизнь просто: умный указатель просто класс , который перегружает ->
и *
операторы. Это означает, что ваш объект семантически выглядит как указатель, но вы можете сделать так, чтобы он делал более крутые вещи, включая подсчет ссылок, автоматическое уничтожение и т. Д.,
shared_ptr
И auto_ptr
в большинстве случаев этого достаточно, но он сопровождается собственным набором небольших идиосинкразий.
Интеллектуальный указатель похож на обычный (типизированный) указатель, например «char *», за исключением случаев, когда сам указатель выходит из области видимости, а затем удаляется также то, на что он указывает. Вы можете использовать его, как обычный указатель, используя «->», но не тогда, когда вам нужен фактический указатель на данные. Для этого вы можете использовать «& * ptr».
Это полезно для:
Объекты, которые должны быть выделены с новым, но вы хотели бы иметь то же время жизни, что и в этом стеке. Если объект назначен интеллектуальному указателю, он будет удален, когда программа выйдет из этой функции / блока.
Данные-члены классов, так что при удалении объекта удаляются также все принадлежащие ему данные, без какого-либо специального кода в деструкторе (вам нужно быть уверенным, что деструктор является виртуальным, что почти всегда полезно) ,
Вы можете не использовать умный указатель, когда:
Смотрите также:
Большинство интеллектуальных указателей обрабатывают указатель на объект для вас. Это очень удобно, потому что вам больше не нужно думать об утилизации предметов вручную.
Наиболее часто используемые смарт - указатели std::tr1::shared_ptr
(или boost::shared_ptr
), и, реже, std::auto_ptr
. Я рекомендую регулярное использование shared_ptr
.
shared_ptr
очень универсален и имеет дело с большим разнообразием сценариев утилизации, включая случаи, когда объекты должны быть «переданы через границы DLL» (распространенный кошмарный случай, если libc
между вашим кодом и DLL используются разные s).
Интеллектуальный указатель - это объект, который действует как указатель, но дополнительно обеспечивает контроль над созданием, уничтожением, копированием, перемещением и разыменованием.
Можно реализовать собственный интеллектуальный указатель, но многие библиотеки также предоставляют реализации интеллектуальных указателей, каждая из которых имеет свои преимущества и недостатки.
Например, Boost предоставляет следующие реализации интеллектуальных указателей:
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
- которое также удаляется при выходе из области.
Вот ссылка для похожих ответов: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html
Умный указатель - это объект, который действует, выглядит и чувствует себя как обычный указатель, но предлагает больше функциональных возможностей. В 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);
http://en.wikipedia.org/wiki/Smart_pointer
В компьютерных науках интеллектуальный указатель - это абстрактный тип данных, который имитирует указатель, предоставляя при этом дополнительные функции, такие как автоматический сбор мусора или проверка границ. Эти дополнительные функции предназначены для уменьшения ошибок, вызванных неправильным использованием указателей при сохранении эффективности. Умные указатели обычно отслеживают объекты, которые на них указывают, с целью управления памятью. Неправильное использование указателей является основным источником ошибок: постоянное распределение, освобождение и ссылки, которые должны выполняться программой, написанной с использованием указателей, очень вероятно, что произойдут некоторые утечки памяти. Умные указатели пытаются предотвратить утечки памяти, делая автоматическое освобождение ресурсов: когда указатель на объект (или последний в ряду указателей) уничтожается,
Пусть T будет классом в этом уроке. Указатели на C ++ можно разделить на 3 типа:
1) Сырые указатели :
T a;
T * _ptr = &a;
Они хранят адрес памяти в ячейке памяти. Используйте с осторожностью, так как программы становятся сложными для отслеживания.
Указатели с постоянными данными или адресом {Read backwards}
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>
}
Общий указатель :
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.
Weak Pointer: Помогает справиться с циклической ссылкой, которая возникает при использовании Shared Pointer. Если у вас есть два объекта, на которые указывают два общих указателя, и есть внутренний общий указатель, указывающий на общий указатель каждого другого, тогда будет циклическая ссылка, и объект не будет быть удаленным, когда общие указатели выходят из области видимости. Чтобы решить эту проблему, измените внутренний член с 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
Ссылки: по сути, они могут быть указателями const, то есть указателем, который является константным и не может быть перемещен с лучшим синтаксисом.
Смотрите: Каковы различия между переменной-указателем и ссылочной переменной в 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_nQ Спасибо Андре за то, что он указал на этот вопрос.
Умный указатель - это класс, оболочка обычного указателя. В отличие от обычных указателей, жизненный цикл интеллектуальной точки основан на подсчете ссылок (сколько раз назначается объект интеллектуального указателя). Поэтому, когда умный указатель назначается другому, внутренний счетчик ссылок плюс плюс. И всякий раз, когда объект выходит из области видимости, счетчик ссылок минус минус.
Автоматический указатель, хотя выглядит похожим, полностью отличается от интеллектуального указателя. Это удобный класс, который освобождает ресурс всякий раз, когда объект автоматического указателя выходит из области видимости переменной. В некоторой степени это делает указатель (на динамически выделенную память) похожим на переменную стека (статически выделяется во время компиляции).
Интеллектуальные указатели - это те, в которых вам не нужно беспокоиться о выделении памяти, распределении ресурсов и передаче.
Вы можете очень хорошо использовать эти указатели таким же образом, как любое распределение работает в Java. В java сборщик мусора делает трюк, в то время как в умных указателях трюк делают деструкторы.
Существующие ответы хороши, но не охватывают, что делать, если умный указатель не является (полным) ответом на проблему, которую вы пытаетесь решить.
Среди прочего (хорошо объяснено в других ответах) использование умного указателя является возможным решением вопроса. Как использовать абстрактный класс в качестве возвращаемого типа функции? который был отмечен как дубликат этого вопроса. Тем не менее, первый вопрос, который нужно задать, если возникает желание указать абстрактный (или фактически любой) базовый класс в качестве возвращаемого типа в C ++, это «что вы на самом деле имеете в виду?». В документации библиотеки контейнера указателей надстроек есть хорошее обсуждение (с дальнейшими ссылками) идиоматического объектно-ориентированного программирования на C ++ (и как оно отличается от других языков)., Таким образом, в C ++ вы должны думать о владении. Какие умные указатели помогают вам, но не являются единственным решением или всегда полным решением (они не дают вам полиморфную копию) и не всегда являются решением, которое вы хотите показать в своем интерфейсе (а возвращаемая функция звучит ужасно очень похоже на интерфейс). Например, может быть достаточно вернуть ссылку. Но во всех этих случаях (умный указатель, контейнер указателя или просто возвращение ссылки) вы изменили возвращаемое значение с какой-либо формы ссылки . Если вам действительно нужна копия, вам может потребоваться добавить больше шаблонной «идиомы» или перейти от идиоматического (или иного) ООП в C ++ к более общему полиморфизму с использованием таких библиотек, как Adobe Poly или Boost.TypeErasure.,