c++ shared_ptr详解 - 使用shared_ptr的示例?





shared_ptr头文件 c++弱指针 (7)


通过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一样工作,希望它可以满足你的需求。

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

 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 ** **

编辑1:我也读过一个代码示例,其中包括:

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

我不明白这个语法!




#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完成




将不同的对象添加到同一个容器中的最好方法是使用基于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();



学习使用智能指针是我认为成为一名有能力的C ++程序员最重要的步骤之一。 正如你所知道的,每当你新增一个对象时,你想删除它。

出现的一个问题是,在例外情况下,确保在所有可能的执行路径中始终只释放一次对象可能非常困难。

这是RAII的原因: http://en.wikipedia.org/wiki/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超出范围,我们都会泄漏添加到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超出范围时,内存自动回收。

作为一个我在团队中困扰新手的练习,是要求他们写自己的智能指针课程。 然后,你完成后立即丢弃该类,并不再使用它。 希望你获得关于智能指针如何工作的关键知识。 真的没有魔法。




我将添加关于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 ,并且它超出了范围,当这个类可能仍然需要它时,这个对象将被释放。




使用shared_ptr vector可以消除内存泄漏的可能性,因为您忘记了在每个元素上遍历向量并调用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 “最佳实践”解释了为什么分配到一个独立的对象而不是临时对象是一个最佳实践

    vec.push_back(ptr);

这会在矢量中创建一个新的共享指针,并将ptr复制到该指针中。 shared_ptr内部引用计数可确保将ptr中的分配对象安全地传输到向量中。

没有解释的是, shared_ptr<gate>的析构函数确保分配的内存被删除。 这是避免内存泄漏的地方。 std::vector<T>的析构函数确保为向量中存储的每个元素调用T的析构函数。 但是,指针的析构函数(例如gate*不会删除已分配的内存 。 这就是你想通过使用shared_ptrptr_vector来避免的。




在libstdc ++的std::function我们使用一个适当大小并对齐的union类型来存储指针,函数指针或指向成员函数的指针。 我们避免为可以存储在该大小和对齐中的任何函数对象进行堆分配, 仅限于它是“位置不变”的

/**
 *  Trait identifying "location-invariant" types, meaning that the
 *  address of the object (or any of its members) will not escape.
 *  Also implies a trivial copy constructor and assignment operator.
 */

代码基于std::tr1::function实现,该部分没有显着变化。 我认为可以使用std::aligned_storage简化,并且可以通过专门化特性来改进,以便将更多类型识别为位置不变。

调用目标对象是在没有任何虚函数调用的情况下完成的,类型擦除是通过在std::function存储单个函数指针来完成的,该函数指针是函数模板特化的地址。 所有操作都是通过存储的指针调用该函数模板并传入一个枚举来识别它被要求执行的操作来完成的。 这意味着没有vtable,只需要在对象中存储单个函数指针。

这个设计是由原始的boost::function作者提供的,我相信它接近于boost实现。 有关基本原理,请参阅Boost.Function的Performance文档。 这意味着GCC的std::functionboost::function更快,因为它是同一个人的类似设计。

注意我们的std::function不支持使用allocator进行构造,它需要做的任何分配都将使用new来完成。

为了回应Emile的评论表达希望避免为std::function保存指向成员函数和对象的指针的堆分配,这里有点黑客(但你没有听到我的意思;-)

struct A {
  int i = 0;
  int foo() const { return 0; }
};

struct InvokeA
{
  int operator()() const { return a->foo(); }
  A* a;
};

namespace std
{
  template<> struct __is_location_invariant<InvokeA>
  { static const bool value = true; };
}

int main()
{
  A a;
  InvokeA inv{ &a };

  std::function<int()> f2(inv);

  return f2();
}

诀窍是InvokeA足够小以适应function的小对象缓冲区,并且特征特化说它可以安全地存储在那里,因此该function直接保存该对象的副本,而不是堆。 只要指向它的指针仍然存在,这就要求持久化,但如果function的目标是bind(&A::foo, &a) ,那么无论如何都是如此。







c++ boost vector shared-ptr smart-pointers