我正在写一个充满乐趣和教育的容器类。以前在编写容器类时,我只限于几个非常基本的方法:GetValue
,SetValue
,GetSize
和Resize
。我这样做是为了避免“代码意大利面”,所以我的课程将更容易调试。
然而,在我看来,班级的用户可能想要做的不仅仅是简单的替换。所以我添加了一些方法:
void Replace(const std::size_t Start, const std::size_t End, const T Value);
void Replace(const std::size_t Start, const std::size_t End, const MyClass Other);
void Insert(const std::size_t Index, const T Value);
void Insert(const std::size_t Index, const MyClass Other);
void Delete(const std::size_t Index);
void Delete(const std::size_t Start, const std::size_t End);
一般来说,类应该只提供最基本的接口,让类的用户自己做功能来做复杂的事情吗?或者是否应该以可维护性为代价内置复杂的东西?
答案 0 :(得分:2)
类应该只提供成员函数的基本/最小接口(最好没有数据!)。然后,您可以将便捷方法添加为非朋友非成员功能。但是,根据接口原理,这些函数仍然是类接口的一部分。
您已经为此命名了主要原因:它使类更容易维护。此外,实施“convienence”方法部分将是一个很好的测试,看看你的界面是否足够好。
请注意,容器的成员函数部分通常应该非常通用且功能强大,而且不仅仅是维护类不变量。
就我所知,这是关于这个问题的最现代意见。它在Scott Meyer的“Effective C ++”(最新的第3版)以及Sutter和Alexandrescu的“C ++编码标准”中得到了突出的提倡。
答案 1 :(得分:2)
问题是,只要你编写另一个容器类(它们中有很多容器类,你可能需要不同类型),你会发现你的设计正方形为O(N * M),其中N是容器类的数量和M算法的数量。
解决方案是将容器与算法分离,这就是在STL中引入迭代器的原因。
有迭代器的替代品,例如使用。多态性。您可以在抽象公共基类中分解遍历接口,并根据它来实现算法。
简而言之,保持容器类中最多的逻辑。
答案 2 :(得分:2)
您应该尝试保持您的界面精简,特别是如果您可能想要实现不同的容器类型,例如基于数组和链表。如果在所有容器中提供一些基本方法,则可以创建执行某些任务但可以在所有容器上运行的外部算法:
void Replace(const std::size_t Start, const std::size_t End, const T Value);
可能会成为
template<class ContainerType>
void ReplaceAllElementsInContainer(ContainerType& Container, const std::size_t Start, const std::size_t End, const T Value);
课外。如果你不这样做,你必须在所有容器中写下所有这些方法。
另一种可能性是使用模板方法模式(与C ++模板无关)并在基类中编写所有这些方法(将基本方法定义为纯虚拟,并从实现的方式调用它们&#34;方便&#34 ; 方法)。这导致可能出现许多虚函数调用,出于性能原因,这些调用可能在容器类中是不可取的。
答案 3 :(得分:1)
我有类似的情况。我的建议是,你有2个“基类”或“超类”。
第一个类,非常通用的类,代表所有容器类的“概念根”,几乎不是类似于接口的方法,应该是:
class Container
{
protected:
int GetValue();
void SetValue(int newValue);
size_t GetSize();
void Resize(size_t);
};
第二节课开始变得不那么概念化,更“现实世界”:
#include "containers.hpp";
class MethodContainer: public Container
{
protected:
void Replace(const std::size_t Start, const std::size_t End, const T Value);
void Replace(const std::size_t Start, const std::size_t End, const MyClass Other);
void Insert(const std::size_t Index, const T Value);
void Insert(const std::size_t Index, const MyClass Other);
void Delete(const std::size_t Index);
void Delete(const std::size_t Start, const std::size_t End);
最后,一些具体的课程:
#include "containers.hpp";
#include "mcontainers.hpp";
#define pointer void*
class Stack: public MethodContainer
{
public:
// these methods use "mcontainer::Insert", "mcontainer::Replace", etc
void Push(pointer Item);
void Pop();
pointer Extract();
}
我有一个带有一组库的应用程序,其中包含一些容器/集合。它是在另一个程序中制作的。 langr。并需要它将其迁移到C ++。 Altought,我还检查了c ++标准库,我结束了将库迁移到C ++,因为我有几个库调用我的容器库,需要它快速完成。
使用“基类”时,您可能希望“保护”其成员,并在子类中“将其带到公众”。除非必要,否则我通常不会制作“私人”字段或方法。
总结:一些非常常见的复杂内容(如内存分配或存储)可能会在您的基类中完成,但是,大部分复杂性应该留给子类。
答案 4 :(得分:0)
如果您的代码中仅由您使用此容器,并且您的界面方法足以满足特定目的,则可以通过这种方式进行。
但是,只要其他人打算使用容器,或者您计划在其他区域使用它,我建议添加使用迭代器类型的接口方法,那么您的容器更加开放,可以与其一起使用stdlib容器和算法。以stdlib容器的接口为例。