正确的 API 用于访问容器 <T> 的数据成员
Proper API for access data members of container<T>
我有以下 class:
class Document
{
public:
Document():
// default values for members,
// ...
m_dirty{false}{}
// Accessor functions
template<class OutputStream>
Document& save(OutputStream stream)
{
// Write stuff to `stream`
// ...
m_dirty = false;
return *this;
}
bool dirty() const { return m_dirty; }
private:
Size2d m_canvas_size;
LayerStack m_layers;
LayerIndex m_current_layer;
std::vector<Palette> m_palettes;
PaletteIndex m_current_palette;
ColorIndex m_current_color;
std::vector<std::string> m_palette_names;
std::vector<std::string> m_layer_names;
bool m_dirty;
};
class 是否应该有 public 成员函数来直接修改 say m_palettes 的元素,比如
Document& color(PaletteIndex, ColorIndex, Color)
,还是更“正确”,只允许通过一对 API:s
访问整个向量
std::vector<Palette> const& palettes();
Document& palettes(std::vector<Palette>&&);
第一个选项会更有效,因为它不需要创建数据成员的临时副本,但持续使用此设计会使界面臃肿。 class.
中的每个容器都需要“深度”getter 和 setter
注意脏标志。因此,以下将打破抽象:
std::vector<Palette>& palettes();
您可能有 Proxy 从 Palette 修改中“传播”脏标志,例如:
template <typename T>
class DirtyProxy
{
T& data;
bool& dirty;
public:
DirtyProxy(T& data, bool& dirty) : data(data), dirty(dirty) {}
~DirtyProxy() { dirty = true;}
DirtyProxy(const DirtyProxy&) = delete;
T* operator ->() { return data; }
};
然后
DirtyProxy<Palette> palette(std::size_t i) { return {m_palettes.at(i), dirty}; }
我认为解决它的最稳健的方法是使用回调。代理的一个问题是它不会处理客户端代码抛出异常的情况(假设有强异常保证)。测试用例:
try
{
auto property_proxy = obj.getProperty();
// an exception is thrown here...
property_proxy->val = x; // Never updated
}
catch(...)
{}
assert(!obj.dirty());
会失败,因为 dtor 总是设置脏标志。但是有回调
class Foo
{
public:
template<class F>
Foo& modifyInSitu(F&& f)
{
f(x);
m_dirty = true;
return *this
}
};
只会在 f(x)
不抛出时更新 m_dirty
。
我有以下 class:
class Document
{
public:
Document():
// default values for members,
// ...
m_dirty{false}{}
// Accessor functions
template<class OutputStream>
Document& save(OutputStream stream)
{
// Write stuff to `stream`
// ...
m_dirty = false;
return *this;
}
bool dirty() const { return m_dirty; }
private:
Size2d m_canvas_size;
LayerStack m_layers;
LayerIndex m_current_layer;
std::vector<Palette> m_palettes;
PaletteIndex m_current_palette;
ColorIndex m_current_color;
std::vector<std::string> m_palette_names;
std::vector<std::string> m_layer_names;
bool m_dirty;
};
class 是否应该有 public 成员函数来直接修改 say m_palettes 的元素,比如
Document& color(PaletteIndex, ColorIndex, Color)
,还是更“正确”,只允许通过一对 API:s
访问整个向量std::vector<Palette> const& palettes();
Document& palettes(std::vector<Palette>&&);
第一个选项会更有效,因为它不需要创建数据成员的临时副本,但持续使用此设计会使界面臃肿。 class.
中的每个容器都需要“深度”getter 和 setter注意脏标志。因此,以下将打破抽象:
std::vector<Palette>& palettes();
您可能有 Proxy 从 Palette 修改中“传播”脏标志,例如:
template <typename T>
class DirtyProxy
{
T& data;
bool& dirty;
public:
DirtyProxy(T& data, bool& dirty) : data(data), dirty(dirty) {}
~DirtyProxy() { dirty = true;}
DirtyProxy(const DirtyProxy&) = delete;
T* operator ->() { return data; }
};
然后
DirtyProxy<Palette> palette(std::size_t i) { return {m_palettes.at(i), dirty}; }
我认为解决它的最稳健的方法是使用回调。代理的一个问题是它不会处理客户端代码抛出异常的情况(假设有强异常保证)。测试用例:
try
{
auto property_proxy = obj.getProperty();
// an exception is thrown here...
property_proxy->val = x; // Never updated
}
catch(...)
{}
assert(!obj.dirty());
会失败,因为 dtor 总是设置脏标志。但是有回调
class Foo
{
public:
template<class F>
Foo& modifyInSitu(F&& f)
{
f(x);
m_dirty = true;
return *this
}
};
只会在 f(x)
不抛出时更新 m_dirty
。