2015-02-23 2 views
1

Я создаю систему частиц, и я борюсь с тем, как структурировать свой код. Идея состоит в том, что пользователь может создать один или несколько объектов ParticleEmitter, которые передаются объекту ParticleManager через объект ofxCurlNoise.Ошибка сегментации при использовании shared_ptr

Теперь, когда пользователь обновляет объекты 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

Цитирование SO рекомендация: «Вопросы ищет отладочная помощь должна включать в себя желаемое поведение, конкретную проблему или ошибку и кратчайший код, необходимый для воспроизведения в самом вопросе. Вопросы без четкого заявления о проблемах не полезны другим читателям ». –

+1

@UlrichEckhardt Этот * * действительно содержит как код, необходимый для идентификации ошибки, так и достаточно четкую постановку задачи. Это не звездный вопрос, но я видел хуже. – Angew

+0

В 'std :: shared_ptr' вы можете хранить только динамически созданные объекты (по' new') ... Из того, что я вижу - вы переходите к 'std :: shared_ptr', что находится в' vector '.. – PiotrNycz

ответ

8

Это не так, как std::shared_ptr работает. Вы создаете свои экземпляры ParticleEmitter в стеке, но std::shared_ptr используется для управления экземплярами, созданными в куче. В вашем коде, когда вы добавляете новый эмиттер в ParticleManager и завертываете его в общий указатель, эмиттер уничтожается при уничтожении вектора particleEmitters (когда, в свою очередь, ваш экземпляр ofApp уничтожается) и, таким образом, уничтожается независимо.

Когда уничтожен экземпляр ofApp, оба экземпляра ofxCurlNoise и particleEmitters уничтожены (в указанном порядке). Поэтому inxCurlNoise, в свою очередь, уничтожит particleManager, который управляет вашими указателями общего доступа, которые затем удаляют ваши излучатели частиц (которые первоначально были созданы в стеке). После всего этого вектор particleEmitters будет уничтожен, а система времени выполнения попытается снова уничтожить ваши излучатели частиц, что приведет к ошибке, которую вы видите.

Кроме того, общие указатели используются для моделирования семантики совместного использования, которую я не вижу в вашем прецеденте. Я думаю, вам будет лучше использовать std::unique_ptr для управления экземплярами, созданными в куче, или вообще не использовать интеллектуальные указатели и создавать все в стеке (которые вы уже почти делаете).

+0

+1, с незначительным nitpick: OP указывает на 'shared_ptr' на элементы в 'vector', а не на локальные переменные. – Angew

+0

Хорошо, теперь я понимаю.Так что shared_ptr не подходит для этого. Но дело в том, что я бы не хотел использовать указатели на объекты ParticleEmitters в классе 'ofApp'. Однако изменения, внесенные в 'ParticleEmitter' в' ofApp', должны быть видны в 'ParticleManager'. Как я могу это достичь? –

+0

Согласен - я уточню, что мой ответ будет более точным. –

2

Вы никогда не должны создавать shared_ptr из обычного указателя, как вы здесь:

shared_ptr<ParticleEmitter>(&e) 

Это пытается освободить ParticleEmitter дважды. Как только вектор, содержащий объекты ParticleEmitter, выходит из области видимости, и один раз, когда shared_ptr выходит за рамки.

+0

Что такое обычный указатель ?? – galinette

+0

Встроенный указатель, например. ParticleEmitter *, а не смарт-указатель. –

+0

Тогда вам понадобится обычный указатель при создании shared_ptr, нет другого способа его использования. – galinette

0
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> указывает на неправильный адрес. Еще раз, когда вектор выходит из облачных областей, разрушается.

+0

'e' - это не объект стека, это ссылка на объект, управляемый std :: vector – galinette

+0

Он не находится в стеке, но выглядит так, как есть. В дополнение к уничтожению, управляемому вектором, он также перемещается в другую ячейку памяти, когда изменяется размер вектора. –

0

При передаче указателя на shared_ptr последний берет на себя ответственность и управляет им. Когда вы передаете указатель на объект, уже управляемый std :: vector, он будет удален дважды раньше или позже, что не может работать, конечно. shared_ptr должен быть передан указатель, который еще не управляется другим классом.

Таким образом, вместо:

shared_ptr<ParticleEmitter>(&e) 

Вы должны создать копию ParticleEmitter объекта

Использование:

shared_ptr<ParticleEmitter>(new ParticleEmitter(e)) 

Или лучше:

std::make_shared<ParticleEmitter>(e) 

Оба метода повторного query ParticleEmitter, чтобы иметь конструктор копирования.

Если ParticleEmitter тяжелый класс, и вы хотите, чтобы избежать глубокой копии, то необходимо реализовать перемещение семантики (шаг конструктора) и использование:

std::make_shared<ParticleEmitter>(std::move(e)) 
Смежные вопросы