Пример использования общего 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<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 );
...........
}


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

664   7  

7 ответов:

С помощью 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 "Лучшие Практики" объясните, почему это лучшие практики выделить в самостоятельный объект, а не временный.

    vec.push_back(ptr);

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

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

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

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, и он выпадает из области видимости, объект будет освобожден, когда класс все еще может понадобиться.

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

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

это причина для RAII:http://en.wikipedia.org/wiki/RAII

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

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

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

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

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

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

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

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

через 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, надеется, что он будет работать для ваших нужд.

#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 был удален, но указатель все еще действителен. выход, ./испытание _obj1: 10 _obj2: 10 _obj2: 10 сделано

лучший способ добавить разные объекты в один контейнер-использовать цикл 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();

Comments

    Ничего не найдено.