Привет, я сегодня задал вопрос о том, как вставлять разные типы объектов в один и тот же векторный массив, и мой код в этом вопросе был
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();
}
но затем он и многие другие предложили мне использовать контейнеры указателей Boost
или 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 );
...........
}
И я не понимаю синтаксиса!
main
создает вектор, который может содержать общие указатели на вызываемый типFoo
; второй создаетFoo
использованиеnew
и общий указатель для управления им; третий помещает копию общего указателя в вектор.Ответы:
Использование a
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
. Причина, по которой это делается отдельно, заключается в том, чтобы предотвратить проблему, которая может возникнуть, если операция вызывает ошибку. В этом примере это невозможно. « Рекомендацииshared_ptr
» Boost объясняют, почему лучше всего выделить в отдельный объект, а не во временный.vec.push_back(ptr);
Это создает новый общий указатель в векторе и копирует
ptr
в него. Подсчет ссылокshared_ptr
внутри гарантирует, что выделенный объект внутриptr
безопасно передается в вектор.Что не объясняется, так это то, что деструктор для
shared_ptr<gate>
обеспечивает удаление выделенной памяти. Здесь можно избежать утечки памяти. Деструктор forstd::vector<T>
гарантирует, что деструктор forT
вызывается для каждого элемента, хранящегося в векторе. Однако деструктор для указателя (например,gate*
) не удаляет выделенную память . Это то, чего вы пытаетесь избежать, используяshared_ptr
илиptr_vector
.источник
int x(5);
для инициализацииx
значением 5. В этом случае оно инициализируется значением нового-выражения, которое создаетANDgate
; значение нового выражения - это указатель на новый объект.Я добавлю , что одна из важных вещей , о
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
где угодно, потому что вы никогда не знаете, когда общий указатель, который его хранит, может его разрушить, и вы получите ошибку.То же самое касается указателей, возвращаемых вам другими классами. Если класс, который вы не писали, передает вам указатель, обычно небезопасно просто помещать его в
shared_ptr
. Нет, если вы не уверены, что класс больше не использует этот объект. Потому что, если вы поместите его в ashared_ptr
, и он выпадет из области видимости, объект будет освобожден, когда классу он все еще может понадобиться.источник
auto t_ptr = make_shared<Type>(...);
или иначеshared_ptr<Type> t_ptr = make_shared<Type>(...);
, просто потому, что эта форма более эффективна.,
междуt_sptr
иptrT
внутри должна быть запятаяshared_ptr<Type> t_sptr ptrT(t_ptr);
?На мой взгляд, обучение использованию умных указателей - один из самых важных шагов, чтобы стать компетентным программистом на 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 выходит за пределы области видимости, память автоматически восстанавливается.
В качестве упражнения, которым я досадил новичкам в моей команде, я попросил их написать свой собственный класс интеллектуального указателя. Затем, когда вы закончите, немедленно откажитесь от класса и никогда не используйте его снова. Надеюсь, вы приобрели важные знания о том, как работает интеллектуальный указатель. На самом деле волшебства нет.
источник
for( auto itt = G.begin(); itt != G.end(); ++itt ){ itt->Run(); }
Документация по 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 был удален, но указатель все еще действителен. вывод ./test _obj1: 10 _obj2: 10 _obj2: 10 done
источник
Лучший способ добавить разные объекты в один и тот же контейнер - использовать make_shared, vector и цикл на основе диапазона, и вы получите красивый, чистый и «читаемый» код!
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();
источник