使用shared_ptr的例子?


82

嗨,我今天问一个问题,关于如何在同一向量数组中插入不同类型的对象,我在该问题中的代码是

 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_vectorptr_listptr_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 );
...........
}

而且我不懂语法!


2
您不了解哪种语法?的第一行main创建一个向量,该向量可以包含指向一个称为类型的共享指针Foo。第二个创建一个Foousing new,以及一个共享的指针来管理它;第三个将共享指针的副本放入向量中。
Mike Seymour 2010年

Answers:


116

使用vectorofshared_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“最佳实践”解释了为什么这是一个最好的做法来分配到一个独立的对象,而不是暂时的。

    vec.push_back(ptr);

这将在向量中创建一个新的共享指针并将其复制ptr到其中。引用中的引用shared_ptr确保了内部分配的对象ptr被安全地转移到向量中。

没有解释的是,析构函数用于shared_ptr<gate>确保删除分配的内存。这是避免内存泄漏的地方。析构函数forstd::vector<T>确保对于T向量中存储的每个元素都调用析构函数for 。但是,指针的析构函数(例如gate*不会删除您已分配的内存。这就是您尝试通过使用shared_ptr或避免的ptr_vector


1
那很详细:)。我的问题是关于代码gate_ptr ptr(new ANDgate)的第三行;对我来说,这不是很熟悉,共享指针门类型的ptr,然后在括号之间发送了一个新的ANDgate!那令人困惑。
艾哈迈德(Ahmed)2010年

6
@Ahmed:整个表达式是一个变量初始化,就像您可能会写int x(5);x值5初始化。在这种情况下,它是使用new表达式的值进行初始化的,该新表达式创建了一个ANDgate;。new表达式的值是指向新对象的指针。
Mike Seymour 2010年

42

我将增加约重要的事情是一个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任何地方使用都是很危险的,因为您永远不知道拥有它的共享指针何时会破坏它,并且会出现段错误。

其他类返回给您的指针也是如此。如果您没有编写一个类,它会为您提供一个指针,通常将其放在.s中是不安全的shared_ptr。除非您确定该类不再使用该对象,否则不要这样做。因为如果您将其放在中shared_ptr,并且超出了范围,则在类可能仍需要该对象时,该对象将被释放。


8
Ken所说的一切都是真实的,但我认为现在首选的称呼方式是auto t_ptr = make_shared<Type>(...);或等效shared_ptr<Type> t_ptr = make_shared<Type>(...);,只是因为这种形式更有效。
杰森·塞德斯

@KenSimon,在那里应该是一个逗号,之间t_sptrptrTshared_ptr<Type> t_sptr ptrT(t_ptr);
阿兰昆兹

除了示例代码中的模棱两可之外,还提供了很好的警告-但您必须要羞耻,因为第一种形式非常整洁,而且也许更重要的是,使用智能指针的任何人都肯定知道它的存在正是为了避免危险的原始操作指针四处漂浮。最后一段很有趣;值得庆幸的是,我尚未与任何强迫我使用原始或不清楚类型的点的库一起工作,尽管我确定它会在一段时间内发生。
underscore_d

20

在我看来,学习使用智能指针是成为合格的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专用于指针类型的容器。我暂时不理them它们。

因此,我们可以从您的示例开始:

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的2个对象。让我们使用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超出范围时,将自动回收内存。

作为困扰我团队中新手的一项练习,我要求他们编写自己的智能指针类。然后,完成操作后立即丢弃该类,不再使用它。希望您获得了有关智能指针在幕后如何工作的关键知识。真的没有魔术。


我的老师给了我关于编写自己的课程的类似建议,因此我将尽力尝试。TY。
艾哈迈德(Ahmed)2010年

您应该使用迭代器来运行所有闸门for( auto itt = G.begin(); itt != G.end(); ++itt ){ itt->Run(); }
GuillaumeMassé2013年

1
或更好的是C ++中的新“ foreach”
只是另一个元编程人员2013年

2

Boost文档提供了一个很好的入门示例: shared_ptr示例(实际上是关于智能指针的向量)或 shared_ptr doc Johannes Schaub的以下回答很好地解释了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 <>将为您处理以上内容-意味着它将在其存储的指针后面释放内存。


shared_ptr是一个智能指针-普通指针的闪亮“包装器”,可以说为指针类型添加了一些AI。ptr_vector是指针的智能容器-指针容器的“包装器”。
celavek

那么ptr_vector是法线向量的替代吗?
艾哈迈德(Ahmed)2010年

@艾默德,我想你可以这样想。
celavek

2

通过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相似),希望它能满足您的需求。


1
#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

将不同对象添加到同一容器中的最佳方法是使用基于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();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.