Пример использования shared_ptr? - PullRequest
81 голосов
/ 13 августа 2010

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

 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<gate*> G;
G.push_back(new ANDgate); 
G.push_back(new ORgate);
for(unsigned i=0;i<G.size();++i)
{
  G[i]->Run();
}

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

Edit1: я тоже читал пример кода, который включал:

typedef boost::shared_ptr<Foo> FooPtr;
.......
int main()
{
  std::vector<FooPtr>         foo_vector;
........
FooPtr foo_ptr( new Foo( 2 ) );
  foo_vector.push_back( foo_ptr );
...........
}

И я не понимаю синтаксис!

Ответы [ 7 ]

114 голосов
/ 13 августа 2010

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

typedef boost::shared_ptr<gate> gate_ptr;

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

    std::vector<gate_ptr> vec;

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

    gate_ptr ptr(new ANDgate);

Выделите новый экземпляр ANDgate и сохраните его в shared_ptr.Причина для того, чтобы сделать это отдельно, состоит в том, чтобы предотвратить проблему, которая может возникнуть в случае сброса операции.Это невозможно в этом примере. Boost shared_ptr "Best Practices" объясняют, почему Best Practice выделяют в отдельно стоящий объект вместо временного.

    vec.push_back(ptr);

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

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

40 голосов
/ 13 августа 2010

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

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

19 голосов
/ 13 августа 2010

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

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

2 голосов
/ 29 мая 2013

С помощью Boost вы можете сделать это>

std::vector<boost::any> vecobj;
    boost::shared_ptr<string> sharedString1(new string("abcdxyz!"));    
    boost::shared_ptr<int> sharedint1(new int(10));
    vecobj.push_back(sharedString1);
    vecobj.push_back(sharedint1);

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

2 голосов
/ 13 августа 2010

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

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

1 голос
/ 10 декабря 2013
#include <memory>
#include <iostream>

class SharedMemory {
    public: 
        SharedMemory(int* x):_capture(x){}
        int* get() { return (_capture.get()); }
    protected:
        std::shared_ptr<int> _capture;
};

int main(int , char**){
    SharedMemory *_obj1= new SharedMemory(new int(10));
    SharedMemory *_obj2 = new SharedMemory(*_obj1);
    std::cout << " _obj1: " << *_obj1->get() << " _obj2: " << *_obj2->get()
    << std::endl;
    delete _obj2;

    std::cout << " _obj1: " << *_obj1->get() << std::endl;
    delete _obj1;
    std::cout << " done " << std::endl;
}

Это пример shared_ptr в действии._obj2 был удален, но указатель все еще действителен.вывод: ./test _obj1: 10 _obj2: 10 _obj2: 10 сделано

0 голосов
/ 31 марта 2018

Лучший способ добавить разные объекты в один и тот же контейнер - это использовать цикл make_shared, vector и range, и у вас будет хороший, чистый и «читаемый» код!

typedef std::shared_ptr<gate> Ptr   
vector<Ptr> myConatiner; 
auto andGate = std::make_shared<ANDgate>();
myConatiner.push_back(andGate );
auto orGate= std::make_shared<ORgate>();
myConatiner.push_back(orGate);

for (auto& element : myConatiner)
    element->run();
...