std::shared_ptr 以此构造

std::shared_ptr constructed from this

我想将 class 的所有对象添加到 std::map。因为我不想复制对象,所以我尝试使用这样的智能指针来解决问题:

Class.h

class A : public std::enable_shared_from_this<A> {
public:
    A();
    ~A();
    static std::map<uint32_t, std::shared_ptr<A>> getObjects();
private:
    static uint32_t counter;
    uint32_t id;
    static std::map<uint32_t, std::shared_ptr<A>> objects;
};

Class.cpp

A::A()
{
    id = counter++;
    std::shared_ptr<A> ptr = std::make_shared<A>();
    objects[id] = ptr->shared_from_this();
}

A::~A()
{
    objects.erase(id);
}

std::map<uint32_t, std::shared_ptr<A>> A::getObjects()
{
    return objects;
}

uint32_t A::counter=0;

std::map<uint32_t, std::shared_ptr<A>> A::objects;

这编译得很好,但是当尝试像这样使用它时

int main(){
    A a;
    A b;
    auto objects = A::getObjects();
}

我得到

Segmentation fault: 11`

如果我尝试将构造函数更改为

A::A()
{
    id = counter++;
    std::shared_ptr<A> ptr(this);
    objects[id] = ptr->shared_from_this();
}

我得到

pointer being freed was not allocated

我基本上需要从 this 构造一个 std::shared_ptr<A> 但无法让它工作。

您无法获得指向不受共享指针管理的对象的拥有共享指针。

自动存储对象的生命周期可能没有由共享指针管理的生命周期。

一般来说,您的设计无法完成,因为对象和对象生命周期是由创建对象的代码控制的,而不是对象本身。

智能 pImpl 包装器可以使对象的内脏具有单独的智能指针确定的生命周期。但即使在那里,共享指针解决方案也是非法的,因为破坏地图会杀死最后一个共享指针,然后它会尝试重新输入地图代码并将其擦除。这是未定义的行为。

弱指针映射内容、基于 pImpl 的包装器、取消注册实例的智能删除器和其他工作的混合可能会起作用。这超出了 SO 答案的范围。

您必须了解如何管理对象生命周期、weak ptr 的工作原理、替代删除器、基于 pImpl 的值类型以及无数其他主题才能实现这一目标。祝你好运。

您的原始代码有堆栈溢出:

A::A()
{
    id = counter++;
    std::shared_ptr<A> ptr = std::make_shared<A>();
    objects[id] = ptr->shared_from_this();
}

您在 A 的构造函数中无条件地构造了 A

您随后尝试的方法也不起作用:

A::A()
{
    id = counter++;
    std::shared_ptr<A> ptr(this);
    objects[id] = ptr->shared_from_this();
}

shared_from_this() 仅当此对象的生命周期由 std::shared_ptr.

管理时才有效

您可以选择几个选项。您 可以 使用静态工厂函数而不公开构造函数,但这对您的 class.

来说是一个意想不到的设计

对我来说更清晰的设计是做一些类似于 Pimpl 模式的事情。您可以将 std::shared_ptr 用于 A 的实际实现,并将功能包装在仅包含 shared_ptr.

的对象中

如果你需要一个class的所有实例都存储在一个全局映射中,你可以试试这个:1)隐藏所有构造函数; 2) 仅通过静态 ('factory') 方法创建新对象。在您的情况下,此方法可以 return 一个 shared_ptr (或者说,它可以 return 一个引用, shared_ptr 本身对全局地图是私有的;在这种情况下,您甚至可以使用 unique_ptr).