使用shared_ptr的示例?

时间:2010-08-13 12:48:25

标签: c++ boost vector shared-ptr smart-pointers

您好我今天问了一个关于How to insert different types of objects in the same vector array 的问题,我在该问题中的代码是

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

我不懂语法!

7 个答案:

答案 0 :(得分:113)

使用vector shared_ptr可以消除内存泄漏的可能性,因为您忘记了遍历向量并在每个元素上调用delete。让我们逐行浏览一个稍微修改过的示例版本。

typedef boost::shared_ptr<gate> gate_ptr;

为共享指针类型创建别名。这样可以避免因输入std::vector<boost::shared_ptr<gate> >并忘记关闭大于号之间的空格而导致C ++语言的丑陋。

    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_ptrptr_vector来避免的。

答案 1 :(得分:40)

我将补充说,关于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中,并且它超出了范围,那么当该类可能仍需要它时,该对象将被释放。

答案 2 :(得分:19)

学习使用智能指针是我认为成为一名称职的C ++程序员最重要的步骤之一。如您所知,每当您在某个时刻新建一个对象时,您想要将其删除。

出现的一个问题是,除了异常之外,很难确保在所有可能的执行路径中始终只释放一次对象。

这就是RAII的原因:http://en.wikipedia.org/wiki/RAII

创建一个帮助器类,目的是确保在所有执行路径中始终删除一个对象。

这样的类的示例是:std :: auto_ptr

但有时你喜欢与其他人分享对象。它只应在不再使用它时删除。

为了帮助完成该引用,已经开发了计数策略,但您仍需要手动记住addref和release ref。实质上,这与new / delete相同。

这就是为什么boost开发了boost :: shared_ptr,它的引用计数智能指针,这样你就可以无意间共享对象而不会泄漏内存。

随着C ++ tr1的加入,现在它也被添加到c ++标准中,但它名为std :: tr1 :: shared_ptr&lt;&gt;。

如果可能,我建议使用标准共享指针。 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的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超出范围时,内存会自动回收。

我在团队中困扰新人的练习是要求他们编写自己的智能指针类。然后你完成后立即丢弃课程,再也不用了。希望您获得关于智能指针如何工作的关键知识。真的没有魔力。

答案 3 :(得分:2)

boost文档提供了一个非常好的开始示例: shared_ptr example(它实际上是关于智能指针的矢量)或 shared_ptr doc Johannes Schaub的以下答案很好地解释了提升智能指针: smart pointers explained

背后的想法(尽可能少的话)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);
}

升压:: ptr_vector&LT;&GT;将为您处理上述事实 - 这意味着它将释放它存储的指针后面的内存。

答案 4 :(得分:2)

通过Boost你可以做到 &GT;

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);

&GT; 用于在矢量容器中插入不同的对象类型。而对于访问你必须使用any_cast,其工作方式类似于dynamic_cast,希望它能满足您的需求。

答案 5 :(得分: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已删除,但指针仍然有效。 输出是, 。/测试  _obj1:10 _obj2:10  _obj2:10  完成了

答案 6 :(得分:0)

将不同对象添加到同一个容器中的最佳方法是使用make_shared,vector和基于范围的循环,您将拥有一个漂亮,干净且可读的&#34;代码!

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();