Пример использования 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<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
. Последние три часа я читал об этой теме, но документация кажется мне довольно продвинутой. **** Может ли кто-нибудь дать мне небольшой пример кода использования 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 );
...........
}
И я не понимаю синтаксис!
Ответы
Ответ 1
Использование vector
of shared_ptr
устраняет возможность утечки памяти, потому что вы забыли пропустить вектор и вызвать delete
для каждого элемента. Пройдите через слегка измененную версию примера по строке.
typedef boost::shared_ptr<gate> gate_ptr;
Создайте псевдоним для общего типа указателя. Это позволяет избежать уродства на языке С++, который возникает при вводе 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" объясняют, почему лучше всего выделять свободностоящий объект вместо временного.
vec.push_back(ptr);
Это создает новый общий указатель в векторе и копирует в него ptr
. Ссылочный отсчет в кишках shared_ptr
гарантирует, что выделенный объект внутри ptr
безопасно перенесен в вектор.
Не объясняется, что деструктор для shared_ptr<gate>
гарантирует удаление выделенной памяти. Здесь предотвращается утечка памяти. Деструктор для std::vector<T>
гарантирует, что деструктор для T
вызывается для каждого элемента, сохраненного в векторе. Однако деструктор для указателя (например, gate*
) не удаляет выделенную память. Этого вы пытаетесь избежать, используя shared_ptr
или ptr_vector
.
Ответ 2
Я добавлю, что одна из важных вещей о 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
, и он выпадет из области видимости, объект будет освобожден, когда класс может все еще нуждаться в нем.
Ответ 3
Обучение использованию интеллектуальных указателей - это, на мой взгляд, один из самых важных шагов, чтобы стать компетентным программистом на С++. Как вы знаете, когда вы новый объект в какой-то момент, вы хотите его удалить.
Одна из проблем заключается в том, что с исключениями может быть очень сложно убедиться, что объект всегда выпущен только один раз во всех возможных путях выполнения.
Это причина RAII: http://en.wikipedia.org/wiki/RAII
Создание вспомогательного класса с целью убедиться, что объект всегда удаляется один раз во всех путях выполнения.
Пример такого класса: std:: auto_ptr
Но иногда вам нравится делиться объектами с другими. Его следует удалять только тогда, когда он больше не использует его.
Чтобы помочь с этой ссылкой, стратегии подсчета были разработаны, но вам все равно нужно помнить addref и выпускать ref вручную. По существу это та же проблема, что и новый /delete.
Именно поэтому boost развил boost:: shared_ptr, он ссылается на интеллектуальный указатель, чтобы вы могли обмениваться объектами, а не терять память непреднамеренно.
С добавлением С++ tr1 это теперь добавляется к стандарту С++, но его имя 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 выходит за пределы области, мы пропускаем два объекта, добавленных в 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 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 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 выходит за пределы области памяти, память автоматически восстанавливается.
Как упражнение, которое я преследовал новичков в моей команде, просит их написать собственный класс умных указателей. Затем после того, как вы закончите, немедленно отбросьте класс и никогда не используйте его снова. Надеюсь, вы приобрели важные знания о том, как умный указатель работает под капотом. Там нет волшебства.
Ответ 4
Документация по ускорению обеспечивает довольно хороший пример запуска:
пример 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 < > будет обрабатывать вышеупомянутое для вас - это означает, что он освободит память за указателями, которые он хранит.
Ответ 5
Через 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, надеется, что он будет работать для ваших нужд.
Ответ 6
#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
сделано