[英]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
实例被销毁时, ofxCurlNoise
和particleEmitters
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
使用对象new
或make_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.