使用 shared_ptr 时出现分段错误
Segmentation fault when using a shared_ptr
我正在制作一个粒子系统,但我正在为如何构建我的代码而苦苦挣扎。这个想法是用户可以创建一个或多个 ParticleEmitter
对象,这些对象通过 ofxCurlNoise
对象传递给 ParticleManager
对象。
现在,我希望当用户更新 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);
}
这不是 std::shared_ptr
的工作方式。您正在堆栈上创建 ParticleEmitter
的实例,但 std::shared_ptr
用于管理在堆上创建的实例。在您的代码中,当您向 ParticleManager
添加一个新的发射器并将其包装到一个共享指针中时,发射器会在 particleEmitters
向量被销毁时被销毁(反过来,您的 ofApp
实例被销毁)因此无论如何都会被销毁。
当 ofApp
的实例被销毁时,ofxCurlNoise
和 particleEmitters
的实例都会被销毁(按此顺序)。因此 ofxCurlNoise 将依次破坏管理您的共享指针的 particleManager
,然后它将删除您的粒子发射器(最初是在堆栈上创建的)。完成所有这些后,particleEmitters
矢量将被销毁,运行时系统将再次尝试销毁您的粒子发射器,从而导致您看到的错误。
此外,共享指针用于对共享所有权语义建模,我在您的用例中看不到这一点。我认为您最好使用 std::unique_ptr
来管理在堆上创建的实例,或者根本不使用智能指针并在堆栈上创建所有内容(您几乎已经在做)。
你永远不应该像你在这里做的那样从普通指针创建 shared_ptr:
shared_ptr<ParticleEmitter>(&e)
这会尝试释放 ParticleEmitter 两次。一旦包含 ParticleEmitter 对象的向量超出范围,一旦 shared_ptr 超出范围。
void ofxCurlNoise::setup(vector<ParticleEmitter>& emitters, int n){
for(auto& e : emitters){
particleManager.addEmitter(shared_ptr<ParticleEmitter>(&e));
}
setup(n);
}
看起来你正在从 "stack" 分配共享指针 objects.You 应该使用 new
或 make_shared<ParticleEmitter>
构造 ParticleEmitter
对象,但实际情况是当向量被调整大小并且 ParticleEmitter
被复制到新位置时 shared_ptr<ParticleEmitter>
指向错误的地址。此外,当 vector 超出作用域时,元素会被破坏。
将指针传递给 shared_ptr
时,后者会取得它的所有权并对其进行管理。当你传递一个指向一个已经由 std::vector 管理的对象的指针时,它迟早会被删除两次,这当然是行不通的。 shared_ptr
必须传递一个尚未由另一个 class 管理的指针。
所以代替:
shared_ptr<ParticleEmitter>(&e)
您必须创建 ParticleEmitter
对象的副本
使用:
shared_ptr<ParticleEmitter>(new ParticleEmitter(e))
或更好:
std::make_shared<ParticleEmitter>(e)
这两种方法都需要ParticleEmitter
有一个复制构造函数。
如果 ParticleEmitter
是一个沉重的 class 并且您想避免对其进行深层复制,那么它必须实现移动语义(移动构造函数)并使用:
std::make_shared<ParticleEmitter>(std::move(e))
我正在制作一个粒子系统,但我正在为如何构建我的代码而苦苦挣扎。这个想法是用户可以创建一个或多个 ParticleEmitter
对象,这些对象通过 ofxCurlNoise
对象传递给 ParticleManager
对象。
现在,我希望当用户更新 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);
}
这不是 std::shared_ptr
的工作方式。您正在堆栈上创建 ParticleEmitter
的实例,但 std::shared_ptr
用于管理在堆上创建的实例。在您的代码中,当您向 ParticleManager
添加一个新的发射器并将其包装到一个共享指针中时,发射器会在 particleEmitters
向量被销毁时被销毁(反过来,您的 ofApp
实例被销毁)因此无论如何都会被销毁。
当 ofApp
的实例被销毁时,ofxCurlNoise
和 particleEmitters
的实例都会被销毁(按此顺序)。因此 ofxCurlNoise 将依次破坏管理您的共享指针的 particleManager
,然后它将删除您的粒子发射器(最初是在堆栈上创建的)。完成所有这些后,particleEmitters
矢量将被销毁,运行时系统将再次尝试销毁您的粒子发射器,从而导致您看到的错误。
此外,共享指针用于对共享所有权语义建模,我在您的用例中看不到这一点。我认为您最好使用 std::unique_ptr
来管理在堆上创建的实例,或者根本不使用智能指针并在堆栈上创建所有内容(您几乎已经在做)。
你永远不应该像你在这里做的那样从普通指针创建 shared_ptr:
shared_ptr<ParticleEmitter>(&e)
这会尝试释放 ParticleEmitter 两次。一旦包含 ParticleEmitter 对象的向量超出范围,一旦 shared_ptr 超出范围。
void ofxCurlNoise::setup(vector<ParticleEmitter>& emitters, int n){
for(auto& e : emitters){
particleManager.addEmitter(shared_ptr<ParticleEmitter>(&e));
}
setup(n);
}
看起来你正在从 "stack" 分配共享指针 objects.You 应该使用 new
或 make_shared<ParticleEmitter>
构造 ParticleEmitter
对象,但实际情况是当向量被调整大小并且 ParticleEmitter
被复制到新位置时 shared_ptr<ParticleEmitter>
指向错误的地址。此外,当 vector 超出作用域时,元素会被破坏。
将指针传递给 shared_ptr
时,后者会取得它的所有权并对其进行管理。当你传递一个指向一个已经由 std::vector 管理的对象的指针时,它迟早会被删除两次,这当然是行不通的。 shared_ptr
必须传递一个尚未由另一个 class 管理的指针。
所以代替:
shared_ptr<ParticleEmitter>(&e)
您必须创建 ParticleEmitter
对象的副本
使用:
shared_ptr<ParticleEmitter>(new ParticleEmitter(e))
或更好:
std::make_shared<ParticleEmitter>(e)
这两种方法都需要ParticleEmitter
有一个复制构造函数。
如果 ParticleEmitter
是一个沉重的 class 并且您想避免对其进行深层复制,那么它必须实现移动语义(移动构造函数)并使用:
std::make_shared<ParticleEmitter>(std::move(e))