用于访问容器<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的主要内容,如果未能解决你的问题,请参考以下文章

无法为返回引用的闭包推断适当的生命周期

在构造函数中传递成员访问函数

通过类模板特化访问成员数据

Python基础知识—容器类型

cpp►STL容器->排序容器->set

指向成员数组的静态指针,用于安全的operator []访问