我有一个Engine类,其中包含并拥有一些系统。 Engine类有两个容器,一个 map 和一个 vector 。两者都保持指向系统的指针。

addSystem模板函数应在映射中添加所需类型的新System的指针,而addToPipeline应该添加在 vector 中作为参数传递的System的指针。我为此使用了shared_ptrs,但是我做错了,因为如果我使用addToPipeline函数,则会出现双重释放错误。

这是简化的Engine类:

class Engine
{
public:

        template <class T>
        T& addSystem();

        void addToPipeline(System&);

private:
        std::map<std::type_index, std::shared_ptr<System>> m_systems;
        std::vector<std::shared_ptr<System>> m_pipeline;
};


void Engine::addToPipeline(System& sys)
{
        m_pipeline.push_back(std::shared_ptr<System>(&sys));
}


template <class T>
T& Engine::addSystem()
{
        std::shared_ptr<T> system = std::make_shared<T>();
        auto inserted = m_systems.emplace(typeid(T),system);
        return static_cast<T&>(*(*inserted.first).second);
}

该函数的用法应如下所示:
auto& POSITION_SYSTEM = engine.addSystem<PositionSystem>();
engine.addToPipeline(POSITION_SYSTEM);

任何帮助表示赞赏!

最佳答案

在这一行:

m_pipeline.push_back(std::shared_ptr<System>(&sys));

您正在为已经被管理的对象创建shared_ptr,因为您已经将同一对象包装在另一个智能指针中。因此,您最终为同一个对象获得了两个引用计数,从而获得了双倍的空闲时间。

这不是应使用shared_ptr的方式。相反,您应该从shared_ptr返回一个addSystem,并以addToPipeline作为参数:
void Engine::addToPipeline(std::shared_ptr<System> sys)
{
    m_pipeline.push_back(sys);
}


template <class T>
std::shared_ptr<T> Engine::addSystem()
{
    std::shared_ptr<T> system = std::make_shared<T>();
    m_systems.emplace(typeid(T),system);
    return system; // No need to use the return value of emplace
}
shared_ptr s的想法是,您始终传递shared_ptr而不是使用裸露的指针或引用(除非所有权无关紧要,否则您也可以传递引用)。您必须这样做,因为引用计数器是由智能指针管理的。

编辑:正如rozina指出的:当然,只要没有人试图删除相应的地址,您仍然可以将引用传递给托管对象。如果其他代码对使用某个对象感兴趣,但不关心所有权,那么这实际上可能是更可取的。例如,您可能需要一个公共(public)接口(interface),该接口(interface)允许获取对内部由智能指针管理的某些对象的引用。例如:
class Foo {
public:
    Bar& getBar() {
        return *m_bar;
    }
private:
    std::shared_ptr<Bar> m_bar;
};

只要没有人执行delete &aFoo.getBar(),这就很好了-如果您使用该引用创建新的shared_ptr,就像在原始代码中一样,就会发生这种情况。

关于c++ - 共享所有权双重免费错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23470942/

10-12 20:50