从共享指针的解除引用值获取共享指针

时间:2017-09-23 18:15:06

标签: c++ pointers memory shared-ptr

想象一下有一个共享指针的向量

typedef vector< shared_ptr< classA > > PointerVector;

还有一个B类,它还有一个共享指针向量的成员,以及一个在已经解除引用的共享指针中推回该向量的方法。

ClassB
{
public:
    void add(classA &ref);
private:
    PointerVector vector;
}

假设main使用dereferenced shared_ptr&lt; add函数提供add函数。 classA&gt;实例:

在main中:

shared_ptr< classA > sharedptr ( new classA );
ClassA &ref = *sharedptr;
ClassB B;
B.add(ref);

有没有办法实现add函数来接受解除引用的共享指针并将其转换为初始共享指针?然后把它推回矩阵?

这就是我想要做的事情:

void ClassB::add(classA &ref) {
    shared_ptr< classA > ptr = &ref;
    vector.push_back( ptr );
}

NOTE1:当将classA添加到向量时,我希望共享指针计数增加1。我不需要创建另一个共享指针,我想“找到”对象以前拥有的共享指针。

注2:我不能使用make_shared因为我目前在tr1

2 个答案:

答案 0 :(得分:3)

通常,您不能只是“找到”指向对象的指针,因为如果指向特定对象,则无法跟踪。您应该自己设计这样的机制,或者使用标准库提供的机制:Another useful SO question

步骤1:从std::enable_shared_from_this派生你的类并提供一个成员函数来获取指针:

#include <memory>

struct ClassA: std::enable_shared_from_this<ClassA>
{
    std::shared_ptr<ClassA> get_pointer() 
    {
        return shared_from_this();
    }
    //...
};

第2步:确保您想要获取指针的所有对象都由shared_ptr管理:

std::shared_ptr<ClassA> original_ptr(new ClassA);

步骤3:每当你想获得一个对象的共享指针时,调用步骤1中定义的函数:

void ClassB::add(classA& ref) {
    shared_ptr<classA> ptr = ref.get_pointer();
    vector.push_back( ptr );
}

答案 1 :(得分:2)

有一种方法可以做到这一点,但需要这可能意味着存在设计缺陷。

为什么它不起作用

shared_ptr需要一个参考计数器才能工作。此计数器保存在创建第一个shared_ptr时创建的控制块中。复制shared_ptr时,副本不仅仅复制指向托管对象的指针,它还会复制指向控制块的指针。这允许它访问引用计数器并在它破坏对象时销毁该块。

使用ClassA &的函数无权访问控制块。尝试创建新的shared_ptr将创建一个引用计数为1的新控制块。当任一引用计数器达到0时,该对象将被销毁。因此,它将被销毁两次,这是未定义的行为。

如何运作

正如评论中已提到的那样,std::enable_shared_from_this解决了您的问题。你必须让你的课程从中衍生出来。

ClassA : std::enable_shared_from_this<ClassA> {...}

然后,您可以致电ClassA.shared_from_this()以获取shared_ptr,该shared_ptr是同一对象的现有enable_shared_from_this的副本。

为什么我不推荐使用它

你必须非常小心shared_ptr。 cppreference.com声明

  

允许仅在先前共享的对象上调用shared_from_this,即在由std :: shared_ptr管理的对象上。否则行为是未定义的(直到C ++ 17)抛出std :: bad_weak_ptr(由默认构造的weak_this中的shared_ptr构造函数)(自C ++ 17开始)。

因此,在尝试使用shared_from_this方法从中创建新的ClassA&之前,您必须确保该对象已经共享。

此外,您的函数签名接受shared_ptr。它可以在堆栈上使用参数调用;从堆栈对象创建shared_ptr<ClassA>是危险的。如果您的功能需要共享所有权,则它应该只接受{{1}}以便传达其意图。这使得它自我记录并且在不牺牲任何东西的情况下更难以错误处理。