使用shared_ptr时出现分段错误

时间:2015-02-23 09:48:11

标签: c++ c++11 design-patterns overloading shared-ptr

我正在制作一个粒子系统,我正在努力解决如何构建我的代码。这个想法是用户可以创建一个或多个ParticleEmitter对象,这些对象通过ParticleManager对象传递给ofxCurlNoise对象。

现在,我希望当用户更新ParticleEmitters个对象时,ParticleManager对象会看到所做的更改。所以我使用了共享指针,但是我在不同的时间有分段错误,无论我使用一个ParticleEmitter(程序启动时的分段错误)还是vector<ParticleEmitter>(程序退出时的分段错误)。

这有什么问题?做我正在做的事情的设计模式是什么?

ofApp.h

#include "ofxCurlNoise.h"

class ofApp : public ofBaseApp{

    // ParticleEmitter particleEmitter;
    vector<ParticleEmitter> particleEmitters;
    ofxCurlNoise curlNoise;

    public:
        void setup();

};

ofApp.cpp

#include "ofApp.h"

void ofApp::setup(){
    // This produces a segfault as soon as the program starts
    // particleEmitter.setup();
    // curlNoise.setup(particleEmitter, 1024*256);

    // This produces a segfault when the program exits
    ParticleEmitter emitter;
    emitter.setup();
    particleEmitters.push_back(emitter);
    curlNoise.setup(particleEmitters, 1024*256);    

}

ofxCurlNoise.h

#include "ParticleManager.h"

class ofxCurlNoise {    

    ParticleManager particleManager;

    public:
        void setup(ParticleEmitter& emitter, int n);
        void setup(vector<ParticleEmitter>& emitters, int n);

    private:
        void setup(int n);    

};

ofxCurlNoise.cpp

#include "ofxCurlNoise.h"

void ofxCurlNoise::setup(ParticleEmitter& emitter, int n){
    particleManager.addEmitter(shared_ptr<ParticleEmitter>(&emitter));
    setup(n);
}

void ofxCurlNoise::setup(vector<ParticleEmitter>& emitters, int n){
    for(auto& e : emitters){
        particleManager.addEmitter(shared_ptr<ParticleEmitter>(&e));
    }
    setup(n);
}

void ofxCurlNoise::setup(int n){
    particleManager.setup(n);
}

ParticleManager.h

#include "ParticleEmitter.h"

class ParticleManager{    

    vector<shared_ptr<ParticleEmitter>> emitters;

    public:
        void addEmitter(const shared_ptr<ParticleEmitter>& emitter);
        void setup(int n);
};

ParticleManager.cpp

#include "ParticleManager.h"

void ParticleManager::setup(int n){
    //...
}

void ParticleManager::addEmitter(const shared_ptr<ParticleEmitter>& emitter){
    emitters.push_back(emitter);
}

4 个答案:

答案 0 :(得分:8)

这不是std::shared_ptr的工作原理。您正在堆栈上创建ParticleEmitter的实例,但std::shared_ptr用于管理在堆上创建的实例。在你的代码中,当你向ParticleManager添加一个新的发射器并将其包装到一个共享指针中时,当particleEmitters向量被破坏时(反过来,你的{{1实例被销毁)并因此被破坏。

ofApp的实例被销毁时,ofAppofxCurlNoise的实例都将被销毁(按此顺序)。因此,ofxCurlNoise将依次销毁管理共享指针的particleEmitters,然后删除粒子发射器(最初在堆栈上创建)。完成所有操作后,particleManager向量将被破坏,运行时系统将再次尝试销毁粒子发射器,从而导致您看到的错误。

此外,共享指针用于建模共享所有权语义,我在您的用例中没有看到。我认为你最好使用particleEmitters来管理在堆上创建的实例,或者根本不使用智能指针并在堆栈上创建所有东西(你几乎已经在做了)。

答案 1 :(得分:2)

你不应该像在这里那样从普通指针创建shared_ptr:

 shared_ptr<ParticleEmitter>(&e)

这会尝试释放ParticleEmitter两次。一旦持有ParticleEmitter对象的向量超出范围,一旦shared_ptr超出范围。

答案 2 :(得分:0)

void ofxCurlNoise::setup(vector<ParticleEmitter>& emitters, int n){
   for(auto& e : emitters){
    particleManager.addEmitter(shared_ptr<ParticleEmitter>(&e));
  }
  setup(n);
}

看起来你正在从“堆栈”分配的对象中创建共享指针。你应该使用ParticleEmitternew来构造make_shared<ParticleEmitter>个对象,但是当向量调整大小并且{复制到新位置ParticleEmitter的{​​1}}指向错误的地址。另外,当矢量离开范围的元素时会被破坏。

答案 3 :(得分:0)

将指针传递给shared_ptr时,后者取得它的所有权并对其进行管理。当您将指针传递给已经由std :: vector管理的对象时,它迟早会被删除两次,当然这不起作用。 shared_ptr必须传递一个尚未被另一个类管理的指针。

所以而不是:

shared_ptr<ParticleEmitter>(&e)

您必须创建ParticleEmitter对象

的副本

使用:

shared_ptr<ParticleEmitter>(new ParticleEmitter(e))

或更好:

std::make_shared<ParticleEmitter>(e)

这两种方法都需要ParticleEmitter才能拥有复制构​​造函数。

如果ParticleEmitter是一个繁重的类并且你想避免对其进行深层复制,那么它必须实现移动语义(移动构造函数)并使用:

std::make_shared<ParticleEmitter>(std::move(e))