【发布时间】:2015-01-14 18:31:32
【问题描述】:
考虑下面的代码。
#include <cassert>
#include <stdexcept>
#include <vector>
struct Item {
Item() : m_enabled(false) {}
void enable()
{
m_enabled = true;
notify_observers(); // can throw
}
bool enabled() const {return m_enabled;}
private:
bool m_enabled;
};
struct ItemsContainer {
ItemsContainer() : m_enabled(false) {}
void enable()
{
bool error = false;
for(Item & item : m_items) {
try {
item.enable();
} catch(...) {
error = true;
}
}
m_enabled = true;
if(error) throw std::runtime_error("Failed to enable all items.");
}
bool enabled() const
{
for(Item const & item : m_items) assert(item.enabled() == m_enabled);
return m_enabled;
}
private:
std::vector<Item> m_items;
bool m_enabled;
};
在我的情况下,Item 的实现如图所示(我无法更改),我正在尝试实现ItemsContainer。我不完全知道如何处理异常。
在提议的实现中,启用容器时,即使通知观察者其中一个抛出,我们也会启用所有项目,最后,我们可能会抛出异常以通知调用者存在(至少)其中一位观察员的问题。但是容器的状态被修改了。是否违反直觉?我是否应该尝试通过在失败时禁用已启用的项目并保持初始状态不变来为
ItemsContainer::enable提供强大的异常保证?在
ItemsContainer::enabled中,断言是否合理?由于我无法控制Item,并且没有记录异常保证,因此可能会交换指令m_enabled = true;和notify_observers();。在这种情况下,ItemsContainer::enable可能会破坏容器的内部状态。
这不是我第一次遇到这样的情况。 是否有最佳实践规则?
注意:我将代码示例减少到最少,但实际上,Item::enable 是一个 setter:Item::set_enabled(bool),我想为 ItemsContainer 实现相同的东西。这意味着如果启用失败,可以禁用项目,但这里再次说明,没有指定异常保证。
【问题讨论】:
标签: c++ foreach exception-handling