繁体   English   中英

使用shared_ptr时出现分段错误

[英]Segmentation fault when using a shared_ptr

我正在制作一个粒子系统,并且正在为如何构建代码而苦苦挣扎。 这个想法是,用户可以创建一个或多个“ ParticleEmitter ofxCurlNoise对象,并通过ofxCurlNoise对象将其传递到“ ParticleManager ofxCurlNoise对象。

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

这有什么问题? 有做我想做的设计模式吗?

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

粒子管理器

#include "ParticleEmitter.h"

class ParticleManager{    

    vector<shared_ptr<ParticleEmitter>> emitters;

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

粒子管理器

#include "ParticleManager.h"

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

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

这不是std::shared_ptr工作方式。 您正在堆栈上创建ParticleEmitter的实例,但是std::shared_ptr用于管理在堆上创建的实例。 在您的代码中,将新的发射器添加到ParticleManager并将其包装到共享指针中时,当particleEmitters向量被破坏时(又是您的ofApp实例被破坏),发射器将被破坏,因此无论如何都会被破坏。

ofApp实例被销毁时, ofxCurlNoiseparticleEmitters ofxCurlNoise的实例都被销毁( ofxCurlNoise顺序)。 因此,ofxCurlNoise将破坏销毁共享管理器的particleManager管理器,该粒子管理器将删除您的粒子发射器(最初在堆栈中创建)。 毕竟做到这一点,在particleEmitters向量被破坏掉了,并且运行时系统会再次尝试摧毁你的粒子发射器,从而导致您所看到的错误。

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

您永远不要像在这里那样从普通指针创建shared_ptr:

 shared_ptr<ParticleEmitter>(&e)

这将尝试释放两次粒子发射器。 一次,包含ParticleEmitter对象的向量超出范围,而一次,shared_ptr超出范围。

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

看起来你是从“堆叠”使得共享指针分配objects.You应该建立ParticleEmitter使用对象newmake_shared<ParticleEmitter>但发生的事情是,当矢量大小和ParticleEmitter被复制在新的位置shared_ptr<ParticleEmitter>指向错误地址。 向量超出范围的元素时,另外会被破坏。

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

所以代替:

shared_ptr<ParticleEmitter>(&e)

您必须创建ParticleEmitter对象的副本

采用:

shared_ptr<ParticleEmitter>(new ParticleEmitter(e))

或更好:

std::make_shared<ParticleEmitter>(e)

这两种方法都要求ParticleEmitter具有复制构造函数。

如果ParticleEmitter是一个繁重的类,并且您希望避免对其进行深复制,则它必须实现move语义(move构造函数)并使用:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM