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


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

14 1472

14 ответов:

интеллектуальный указатель-это класс, который обертывает "сырой" (или "голый") указатель 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()
{
    {
       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 экземпляры не могут быть скопированы. Это предотвращает многократное удаление указателя (неправильно). Однако вы можете передавать ссылки на него другим вызываемым функциям.

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

более сложная политика интеллектуального указателя включает подсчет ссылок на указатель. Это позволяет копировать указатель. Когда последняя" ссылка " на объект уничтожается, объект удаляется. Эта политика реализуется 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 определили a weak_ptr чтобы определить слабую (несчетную) ссылку на A 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.

вот простой ответ на эти дни современного C++:

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

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

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

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

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

тема подробно освещена в книге "C++ Templates: The Complete Guide" by David Vandevoorde, Nicolai M. Josuttis глава Глава 20. Умный Указатели. Некоторые темы:

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

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

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

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

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

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

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

Читайте также:

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

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

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

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

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

например, 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, но теперь устарел.

вот ссылка для подобных ответов : http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

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

пример:

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) Raw указатели:

T a;  
T * _ptr = &a; 

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

указатели с данными const или адресом { читать назад }

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

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

  T * const ptr2 ;

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

const T * const ptr3 ; 

константный указатель на константный тип данных . То есть вы не можете либо переместите указатель, либо вы не можете изменить указатель типа данных на указатель. то есть. 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.

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

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

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

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

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

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

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

Я хотел бы добавить еще один пункт к вышеуказанному вопросу, smart pointer std::shared_ptr не имеет оператора индекса и не поддерживает арифметику понтера, мы можем использовать get() для получения встроенного указателя.