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

StackOverflow https://stackoverflow.com/questions/106508

Вопрос

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

Это было полезно?

Решение

Обновить

Этот ответ довольно старый, и поэтому описывает то, что было "хорошим" в то время, а именно умные указатели, предоставляемые библиотекой 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.

СТАРЫЙ ОТВЕТ

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

Интеллектуальные указатели следует предпочесть необработанным указателям.Если вы чувствуете, что вам нужно использовать указатели (сначала подумайте, можете ли вы в самом деле 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()
{
    {
       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 определили weak_ptr чтобы определить слабую (неучтенную) ссылку на shared_ptr.

Другие советы

Вот простой ответ для наших дней современного C ++:

  • Что такое умный указатель?
    Это тип, значения которого можно использовать как указатели, но который предоставляет дополнительную функцию автоматического управления памятью:Когда интеллектуальный указатель больше не используется, память, на которую он указывает, освобождается (см. Также более подробное определение приведено в Википедии).
  • Когда я должен его использовать?
    В коде, который включает отслеживание права собственности на часть памяти, выделение или отмену выделения;интеллектуальный указатель часто избавляет вас от необходимости выполнять эти действия явно.
  • Но какой умный указатель мне следует использовать в каком из этих случаев?
    • Использование std::unique_ptr когда вы не собираетесь хранить несколько ссылок на один и тот же объект.Например, используйте его для указателя на память, которая выделяется при входе в некоторую область и освобождается при выходе из области.
    • Использование std::shared_ptr когда вы действительно хотите ссылаться на свой объект из нескольких мест - и не хотите, чтобы ваш объект был выделен до тех пор, пока все эти ссылки сами не исчезнут.
    • Использование std::weak_ptr когда вы действительно хотите ссылаться на свой объект из нескольких мест - для тех ссылок, которые можно игнорировать и освобождать (поэтому они просто заметят, что объект исчез при попытке разыменования).
    • Не используйте boost:: умные указатели или std::auto_ptr за исключением особых случаев, о которых вы можете прочитать, если потребуется.
  • Эй, я не спрашивал, какой из них использовать!
    Ах, но ты действительно хотел этого, признай это.
  • Итак, когда же мне тогда следует использовать обычные указатели?
    В основном в коде, который не обращает внимания на владение памятью.Обычно это происходит в функциях, которые получают указатель откуда-то еще и не выделяют и не отменяют выделение, а также не сохраняют копию указателя, которая переживает их выполнение.

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

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

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

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

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

  • Защита от исключений
  • Держатели, (примечание, std::auto_ptr является ли реализация такого типа интеллектуального указателя)
  • Получение ресурса - Это Инициализация (Это часто используется для безопасного управления ресурсами в C ++)
  • Ограничения правообладателя
  • Подсчет ссылок
  • Одновременный доступ к Счетчику
  • Уничтожение и Освобождение

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

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

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

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

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

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

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

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

Большинство видов интеллектуальных указателей обрабатывают удаление объекта-указателя за вас.Это очень удобно, потому что вам больше не нужно думать об утилизации объектов вручную.

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

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

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

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

Например, Повышение предоставляет следующие реализации интеллектуального указателя:

  • 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, но сейчас устарел.

Вот ссылка на похожие ответы : 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; 

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

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

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

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

  T * const ptr2 ;

Постоянный указатель на тип данных T .Это означает, что вы не можете переместить указатель, но вы можете изменить значение, на которое указывает указатель.т. е. *ptr2 = 19 сработает, но ptr2++ ; ptr2-- etc не будет работать.Читать задом наперед :постоянный указатель на тип 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 , объект автоматически удаляется, т. е. объект objected удаляется, когда все share_ptr, указывающие на объект, выходят за пределы области видимости.Это избавляет от головной боли, связанной с необходимостью удаления объектов, которые вы выделили с помощью new.

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

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

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

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

Интеллектуальные указатели - это те, с помощью которых вам не нужно беспокоиться о выделении памяти, совместном использовании ресурсов и их передаче.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top