我正在制作一个粒子系统,我正在努力解决如何构建我的代码。这个想法是用户可以创建一个或多个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);
}
答案 0 :(得分:8)
这不是std::shared_ptr
的工作原理。您正在堆栈上创建ParticleEmitter
的实例,但std::shared_ptr
用于管理在堆上创建的实例。在你的代码中,当你向ParticleManager
添加一个新的发射器并将其包装到一个共享指针中时,当particleEmitters
向量被破坏时(反过来,你的{{1实例被销毁)并因此被破坏。
当ofApp
的实例被销毁时,ofApp
和ofxCurlNoise
的实例都将被销毁(按此顺序)。因此,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);
}
看起来你正在从“堆栈”分配的对象中创建共享指针。你应该使用ParticleEmitter
或new
来构造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))