Пример использования shared_ptr

Привет! Сегодня я задал вопрос о Как вставить разные типы объектов в один и тот же векторный массив , и мой код в этом вопросе был

 gate* G[1000];
G[0] = new ANDgate() ;
G[1] = new ORgate;
//gate is a class inherited by ANDgate and ORgate classes
class gate
{
 .....
 ......
 virtual void Run()
   {   //A virtual function
   }
};
class ANDgate :public gate 
  {.....
   .......
   void Run()
   {
    //AND version of Run
   }  

};
 class ORgate :public gate 
  {.....
   .......
   void Run()
   {
    //OR version of Run
   }  

};      
//Running the simulator using overloading concept
 for(...;...;..)
 {
  G[i]->Run() ;  //will run perfectly the right Run for the right Gate type
 } 

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

std::vector G;
G.push_back(new ANDgate); 
G.push_back(new ORgate);
for(unsigned i=0;iRun();
}

, но затем он и многие другие предложили, чтобы я лучше использовал Контейнеры повышения указателя
или shared_ptr . Последние три часа я читал на эту тему, но документация мне кажется довольно продвинутой. **** Может кто-нибудь дать мне небольшой пример кода shared_ptr и почему они предложили использовать shared_ptr . Также есть другие типы, такие как ptr_vector , ptr_list и ptr_deque ** **

Edit1: я также прочитал пример кода, который включал: [12148 И я не Не понимаю синтаксис!

81
задан Community 23 May 2017 в 12:02
поделиться

4 ответа

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

typedef boost::shared_ptr<gate> gate_ptr;

Создайте псевдоним для типа общего указателя. Это позволяет избежать уродства языка C ++, которое возникает в результате ввода std :: vector > и забвения пробела между закрывающими знаками «больше» .

    std::vector<gate_ptr> vec;

Создает пустой вектор из объектов boost :: shared_ptr .

    gate_ptr ptr(new ANDgate);

Выделите новый экземпляр ANDgate и сохраните его в shared_ptr . Причина того, чтобы делать это отдельно, - предотвратить проблему, которая может возникнуть, если операция выдает ошибку. В этом примере это невозможно. Boost shared_ptr «Лучшие практики» объясняют, почему лучшая практика - выделить в отдельный объект вместо временного.

    vec.push_back(ptr);

Это создает новый общий указатель в векторе и копирует в него ptr . Подсчет ссылок в внутренности shared_ptr гарантирует, что выделенный объект внутри ptr безопасно перенесен в вектор.

Что не объясняется, так это то, что деструктор для shared_ptr обеспечивает удаление выделенной памяти. Здесь можно избежать утечки памяти.Деструктор для std :: vector гарантирует, что деструктор для T вызывается для каждого элемента, хранящегося в векторе. Однако деструктор для указателя (например, gate * ) не удаляет выделенную вами память . Это то, чего вы пытаетесь избежать, используя shared_ptr или ptr_vector .

114
ответ дан 24 November 2019 в 09:33
поделиться

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

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

Это причина RAII: http://en.wikipedia.org/wiki/RAII

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

Пример такого класса: std :: auto_ptr

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

Чтобы помочь с этим, были разработаны стратегии подсчета ссылок, но вам все равно нужно запомнить addref и освободить ref вручную. По сути, это та же проблема, что и new / delete.

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

С добавлением C ++ tr1 теперь он также добавлен в стандарт C ++, но имеет имя std :: tr1 :: shared_ptr <>.

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

Итак, мы можем начать с вашего примера:

std::vector<gate*> G; 
G.push_back(new ANDgate);  
G.push_back(new ORgate); 
for(unsigned i=0;i<G.size();++i) 
{ 
  G[i]->Run(); 
} 

Проблема в том, что теперь, когда G выходит за пределы области видимости, мы пропускаем 2 объекта, добавленных к G. Давайте перепишем его, чтобы использовать std :: tr1 :: shared_ptr

// Remember to include <memory> for shared_ptr
// First do an alias for std::tr1::shared_ptr<gate> so we don't have to 
// type that in every place. Call it gate_ptr. This is what typedef does.
typedef std::tr1::shared_ptr<gate> gate_ptr;    
// gate_ptr is now our "smart" pointer. So let's make a vector out of it.
std::vector<gate_ptr> G; 
// these smart_ptrs can't be implicitly created from gate* we have to be explicit about it
// gate_ptr (new ANDgate), it's a good thing:
G.push_back(gate_ptr (new ANDgate));  
G.push_back(gate_ptr (new ORgate)); 
for(unsigned i=0;i<G.size();++i) 
{ 
   G[i]->Run(); 
} 

Когда G выходит за рамки, память автоматически восстанавливается.

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

19
ответ дан 24 November 2019 в 09:33
поделиться

Я добавлю, что один из важных моментов в shared_ptr'ах состоит в том, чтобы только когда-либо конструировать их со следующим синтаксисом:

shared_ptr<Type>(new Type(...));

Таким образом, "настоящий" указатель на Type является анонимным для вашей области видимости и удерживается только общим указателем. Таким образом, вы не сможете случайно использовать этот "настоящий" указатель. Другими словами, никогда не делайте так:

Type* t_ptr = new Type(...);
shared_ptr<Type> t_sptr ptrT(t_ptr);
//t_ptr is still hanging around!  Don't use it!

Хотя это сработает, теперь в вашей функции есть Type* указатель (t_ptr), который живет вне общего указателя. Опасно использовать t_ptr в любом месте, потому что вы никогда не знаете, когда общий указатель, который его содержит, может его уничтожить, и вы получите segfault.

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

40
ответ дан 24 November 2019 в 09:33
поделиться

В документации по boost приводится довольно хороший начальный пример: shared_ptr example (на самом деле речь идет о векторе умных указателей) или shared_ptr doc. Следующий ответ Йоханнеса Шауба довольно хорошо объясняет смарт-указатели boost: smart pointers explained

Идея ptr_vector в том, что он обрабатывает деаллокацию памяти под хранимые указатели за вас: допустим, у вас есть вектор указателей, как в вашем примере. При выходе из приложения или из области видимости, в которой определен вектор, вам придется очистить память после себя (вы динамически выделили ANDgate и ORgate), но просто очистить вектор не получится, потому что вектор хранит указатели, а не реальные объекты (он не уничтожит только то, что содержит).

 // if you just do
 G.clear() // will clear the vector but you'll be left with 2 memory leaks
 ...
// to properly clean the vector and the objects behind it
for (std::vector<gate*>::iterator it = G.begin(); it != G.end(); it++)
{
  delete (*it);
}

boost::ptr_vector<> сделает это за вас - то есть деаллоцирует память под указатели, которые хранит.

2
ответ дан 24 November 2019 в 09:33
поделиться
Другие вопросы по тегам:

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