我最近一直在阅读一些C ++书籍(Sutters,Meyers),这促使我更有效地开始使用智能指针(以及一般的对象破坏)。但现在我不知道如何解决我的问题。 具体来说,我现在有一个继承自Scene和InputListener的IntroScene类。
场景并不真正相关,但是InputListener在构造时订阅了一个InputManager, 并且在破坏时再次取消。
class IntroScene : public sfg::Scene, public sfg::InputListener {
/*structors, inherited methods*/
virtual bool OnEvent(sf::Event&) override; //inputlistener
}
但是现在,如果输入管理器将事件发送到场景,并且场景决定替换自己 因为它,我有一个不再存在的对象上运行的函数。
bool IntroScene::OnEvent(sf::Event& a_Event) {
if (a_Event.type == sf::Event::MouseButtonPressed) {
sfg::Game::Get()->SceneMgr()->Replace(ScenePtr(new IntroScene()));
} //here the returned smartpointer kills the scene/listener
}
附带问题:这有关系吗?我用谷歌搜索,但没有找到肯定的是或否。我知道100% 销毁后的销毁对象上不会调用任何方法。 如果必须的话,我可以将Replace()返回值存储到OnEvent()方法的结尾。
真正的问题是InputListener
InputListener::InputListener() {
Game::Get()->InputMgr()->Subscribe(this);
}
InputListener::~InputListener() {
if (m_Manager) m_Manager->Unsubscribe(this);
}
因为在OnEvent()期间调用它,在HandleEvents()期间由InputManager调用
void InputManager::HandleEvents(EventQueue& a_Events) const {
while (!a_Events.empty()) {
sf::Event& e = a_Events.front();
for (auto& listener : m_Listeners) {
if (listener->OnEvent(e)) //swallow event
break;
}
a_Events.pop();
}
void InputManager::Subscribe(InputListener* a_Listener) {
m_Listeners.insert(a_Listener);
a_Listener->m_Manager = this;
}
void InputManager::Unsubscribe(InputListener* a_Listener) {
m_Listeners.erase(a_Listener);
a_Listener->m_Manager = nullptr;
}
因此,当创建新的Scene + Listener时,以及旧的Scene + Listener被销毁时,在循环期间修改列表m_Listeners。事情就这样破碎了。 我已经考虑过在启动和停止循环时设置一个标志,并存储(un)在单独列表中设置时发生的订阅,并在之后处理。但感觉有点笨拙。
那么,我怎样才能真正重新设计这个以防止这种情况呢?提前谢谢。
编辑,解决方案: 我最终得到了循环标志和延迟条目列表(下面的inetknight'答案) 仅限订阅,因为以后可以安全地完成。
取消订阅必须立即处理,因此我不存储原始指针而是存储一个(指针可变的bool)对(可变,因为一个集只返回一个const_iterator)。当发生这种情况时我将bool设置为false并在事件循环中检查它(请参阅下面的dave评论)。 不确定它是最干净的解决方案,但它就像一个魅力。非常感谢你们
答案 0 :(得分:1)
附带问题:这有关系吗?我用谷歌搜索,但没有找到肯定的是或否。我知道在被销毁的对象被销毁后100%没有被调用的方法。如果必须的话,我可以将Replace()返回值存储到OnEvent()方法的结尾。
如果你知道100%没有调用任何方法,他就破坏了对象,并且没有访问任何成员变量,那么它就是安全。是否意图取决于你。
您可以拥有另一个请求取消/订阅的对象列表。然后,在您告诉事件列表中的每个人之后,您将在继续下一个事件之前处理un / subscription请求列表。
/* this should be a member of InputManager however you did not provide a class definition */
typedef std::pair<InputListener *, bool> SubscriptionRequest;
bool handleEventsActive = false;
std::vector<SubscriptionRequest> deferredSubscriptionRequests;
void InputManager::HandleEvents(EventQueue& a_Events) const {
// process events
handleEventsActive = true;
while (!a_Events.empty()) {
sf::Event& e = a_Events.front();
for (auto& listener : m_Listeners)
{
//swallow event
if (listener->OnEvent(e)) {
break;
}
}
a_Events.pop();
// process deferred subscription requests occurred during event
while ( not deferredSubscriptionRequests.empty() ) {
SubscriptionRequest request = deferredSubscriptionRequests.back();
deferredSubscriptionRequests.pop_back();
DoSubscriptionRequest(request);
}
}
handleEventsActive = false;
}
void InputManager::DoSubscriptionRequest(SubscriptionRequest &request) {
if ( request.second ) {
m_Listeners.insert(request.first);
request.first->m_Manager = this;
} else {
m_Listeners.erase(request.first);
request.first->m_Manager = nullptr;
}
}
void InputManager::Subscribe(InputListener* a_Listener)
{
SubscriptionRequest request{a_Listener, true};
if ( handleEventsActive ) {
deferredSubscriptionRequests.push_back(request);
} else {
DoSubscriptionRequest(request);
}
}
void InputManager::Unsubscribe(InputListener* a_Listener)
{
SubscriptionRequest request{a_Listener, false};
if ( handleEventsActive ) {
deferredSubscriptionRequests.push_back(request);
} else {
DoSubscriptionRequest(request);
}
}