代码之家  ›  专栏  ›  技术社区  ›  nabulke

类设计问题-如何提供对类成员容器的只读访问[已关闭]

c++
  •  7
  • nabulke  · 技术社区  · 15 年前

    在我的日常工作中,我经常发现自己在写这样一个简单的例子:

    class CGarage
    {
    public:
        CGarage();
        ~CGarage();
        typedef std::vector<Car> CarCollection;
    
    private:
        CarCollection m_Cars;
    };
    

    我希望CGarage的用户对CarCollection具有只读访问权限。为了实现这一目标,这些是一些共同的解决办法,但都不是很令人满意:

    解决方案1

    class CGarage
    {
        Car GetCar(CarCollection::size_type index) const;
        CarCollection::size_type CarCount() const;
    };
    

    主要缺点:

    • 由于缺少迭代器,我不能在Cars上使用STL算法(例如for_each(…))

    解决方案2

    class CGarage
    {
        CarCollection::const_iterator CarBegin() const;
        CarCollection::const_iterator CarEnd() const;
        CarCollection::size_type CarCount() const;
    };
    

    主要缺点:

    • 如果您需要对其他迭代器类型(it,reverse-it)的支持,则需要大量样板代码。

    解决方案3

    class CGarage
    {
        const CarCollection GetCars() const;
    };
    

    主要缺点:

    • 按值返回时复制CarCollection的成本
    • 类用户已知的实现细节(例如,在不更改中断用户代码的情况下,不能更改为std::list)

    解决方案4

    class CGarage
    {
        const CarCollection& GetCars() const;
    };
    

    主要缺点:

    • CarCollection引用的生存期绑定到cGrage的生存期
    • 类用户已知的实现详细信息

    问题

    如何提供对CarCollection的只读访问?

    如果CarCollection是一个指向Car的向量,那么您的解决方案会改变吗?

    如果允许对集合的读写访问,则可以将集合公开吗?

    谢谢你的建议

    6 回复  |  直到 15 年前
        1
  •  5
  •   Peter Alexander    15 年前

    如何提供对CarCollection的只读访问?

    我看不出解决方案4有什么问题。对于CGarage的用户来说,显而易见的是,它的汽车收藏与车库的使用寿命有关。如果他们需要收集的汽车比车库的寿命长,那么如果他们愿意的话,他们总是可以复印一份。

    或者,让cGrage保持 shared_ptr 去提车还车,但我不推荐。

    如果CarCollection是一个指向Car的向量,那么您的解决方案会改变吗?

    对于拥有对象的集合(即引用类型),最好使用不同的容器。这个 std:: 容器都是为值类型设计的,不能很好地处理引用类型(尤其是constness)。对于这些,使用类似Boost的 ptr_vector .

    如果允许对集合的读写访问,则可以将集合公开吗?

    取决于你的具体情况。集合的语义是否可能更改?如果没有,那么您可以安全地将其公开(例如。 std::pair ). 不过,我不建议您针对特定于域的问题这样做。

        2
  •  1
  •   AndersK    15 年前

    这样申报还不够吗?

    const CarCollection& Cars() { return m_Cars; }
    

    然后

     CarCollection::const_iterator it = garage.Cars().begin();
    

    应该有用但是

      CarCollection::iterator it = garage.Cars().begin();
    

    会产生错误。

        3
  •  1
  •   icecrime    15 年前

    我会和 解决方案4 . 关于指针容器的常量问题,可以使用 boost::ptr_vector 正确传播康斯特内斯(除其他外)。

        4
  •  1
  •   svick Raja Nadar    14 年前

    这要看情况。

    1. 如果类的客户需要 车辆矢量 他们假设汽车是以任何原因连续存储在内存中的(不过我看不出另一个原因),那么您应该提供一个成员函数,返回对向量的常量引用。不要为传递值和与对象创建相关联的固有异常安全问题而烦恼。如果矢量必须比车库对象活得长,则客户端将生成一个副本。

    2. 现在,在任何其他情况下,最小耦合原则建议我们返回一对迭代器。毕竟,一对迭代器是 收集 . 如果类的客户端需要复杂性假设,则返回正确类型的迭代器(并对其进行文档化)。

    3. 为了方便,以及 只有当它有意义的时候 ,你可以超载 operator[] . 车库是汽车的集合。如果车库内的停车位是编号的,并且您希望按停车位访问汽车,那么这是一个很好的解决方案。

    尽管如此,你说C++迭代器的设计方式迫使你编写大量的样板代码是正确的。然后,在第一次编写类时,返回对向量的常量引用,然后重构。

        5
  •  0
  •   outis    15 年前

    选项4,但返回对抽象基类的引用,该基类包含到标准集合的显式转换运算符。这至少可以减少暴露的实现细节。

    为了进一步减少对实现细节的依赖,请将包含集合的类转换为模板,并参数化集合类型。

        6
  •  0
  •   svick Raja Nadar    14 年前

    我可以回答一个问题:

    如果允许对集合的读写访问,则可以将集合公开吗?

    不一定。如果需要更改实现,或者在变量更改时需要运行一段代码,该怎么办。即使不这样做,将来也可能需要这样做,将公共var更改为set/get可能会破坏很多代码。