在我的日常工作中,我经常发现自己正在编写类似于这个简化示例的类:
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;
};
主要缺点:
解决方案2
class CGarage
{
CarCollection::const_iterator CarBegin() const;
CarCollection::const_iterator CarEnd() const;
CarCollection::size_type CarCount() const;
};
主要缺点:
解决方案3
class CGarage
{
const CarCollection GetCars() const;
};
主要缺点:
解决方案4
class CGarage
{
const CarCollection& GetCars() const;
};
主要缺点:
问题
您如何提供对CarCollection的只读访问权限?
如果CarCollection是一个带Car指针的向量,那么你的解决方案会改变吗?
如果您允许对集合进行读写访问,是否可以公开该集合?
感谢您的任何建议
答案 0 :(得分:5)
您如何提供对CarCollection的只读访问权限?
我不明白解决方案4有什么问题。对于CGarage的用户来说,显而易见的是,对其汽车收藏的参考与车库的使用寿命有关。如果他们需要汽车收藏品比车库更长,那么他们总是可以随意拿一份副本。
或者,让CGarage为汽车收藏品保留一个shared_ptr
并返回,但我不会推荐它。
如果CarCollection是一个带Car指针的向量,那么你的解决方案会改变吗?
对于拥有对象(即引用类型)的集合,最好使用不同的容器。 std::
容器都是为值类型设计的,并且不能很好地处理引用类型(尤其是constness)。对于这些,请使用Boost的ptr_vector。
如果您允许对集合进行读写访问,是否可以公开该集合?
取决于您的具体情况。集合的语义可能会改变吗?如果没有,那么您可以安全地将其公开(例如std::pair
)。我不建议您针对特定领域的问题执行此操作。
答案 1 :(得分:1)
这样声明就不够了吗?
const CarCollection& Cars() { return m_Cars; }
然后
CarCollection::const_iterator it = garage.Cars().begin();
应该可以工作但是
CarCollection::iterator it = garage.Cars().begin();
会给出错误。
答案 2 :(得分:1)
我会选择解决方案4 。关于指针容器的常量问题,你可以使用正确传播constness的boost::ptr_vector
(除其他外)。
答案 3 :(得分:1)
取决于。
如果班级的客户需要矢量汽车,例如。他们假设汽车因任何原因连续存储在内存中(虽然我没有看到其他原因),然后你应该提供一个成员函数,返回一个向量的常量引用。不要为传递价值和与对象创建相关的固有异常安全问题而烦恼。如果向量必须比Garage对象更长,则客户端将进行复制。
现在,在任何其他情况下,最小耦合原则表明我们返回一对迭代器。毕竟,一对迭代器是集合的通用表达式。如果类的客户端需要复杂性假设,那么返回正确类型的迭代器(并记录它)。
为方便起见,仅在有意义时,您可以重载operator[]
。车库是汽车的集合。如果车库内的停车位已经编号,并且您想要通过插槽访问汽车,那么这可能是一个很好的解决方案。
尽管如此,你说C ++迭代器的设计方式迫使你编写大量的样板代码是正确的。然后,当您第一次编写类时,将const引用返回给向量,然后重构。
答案 4 :(得分:0)
我可以回答一个问题:
如果您允许对集合进行读写访问,是否可以公开该集合?
不一定。如果您需要更改实现,或者需要在更改变量时运行一段代码,该怎么办?即使你不这样做,你可能需要将来,并且从public var到set / get的更改可能会破坏很多代码。
答案 5 :(得分:0)
选项4,但返回对包含显式转换运算符到标准集合的抽象基类的引用。这至少会削减实施细节的曝光程度。
为了进一步减少对实现细节的依赖,将包含集合的类转换为模板,参数化集合类型。