用于访问容器<T> 数据成员的适当 API
Posted
技术标签:
【中文标题】用于访问容器<T> 数据成员的适当 API【英文标题】:Proper API for access data members of container<T> 【发布时间】:2020-11-05 08:01:23 【问题描述】:我有以下课程:
class Document
public:
Document():
// default values for members,
// ...
m_dirtyfalse
// 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;
;
该类是否应该具有用于直接修改say m_palettes 元素的公共成员函数,例如
Document& color(PaletteIndex, ColorIndex, Color)
,或者更“正确”,只允许通过一对API访问整个向量:s
std::vector<Palette> const& palettes();
Document& palettes(std::vector<Palette>&&);
第一个选项会更有效,因为它不需要创建数据成员的临时副本,但始终使用这种设计会使接口变得臃肿。对于类中的每个容器,它都需要“深度”的 getter 和 setter。
注意脏标志。因此,以下内容会破坏抽象:
std::vector<Palette>& palettes();
【问题讨论】:
你可能有代理来“传播”来自Palette
修改的脏标志。
@Jarod42 所以基本上是一个具有对容器和脏标志的私有引用的对象,然后是该代理上的适当方法。听起来是个不错的方法。
... 并且代理的析构函数设置了脏标志。但它必须有一个名字。
【参考方案1】:
我认为解决它的最可靠方法是使用回调。代理的一个问题是它无法处理客户端代码抛出异常的情况(假设有很强的异常保证)。测试用例:
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
。
【讨论】:
【参考方案2】:您可能有 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;
【讨论】:
以上是关于用于访问容器<T> 数据成员的适当 API的主要内容,如果未能解决你的问题,请参考以下文章