我有更多的Java背景,因此让我用Java示例来说明。假设存在以下代码:
interface iFoo {
/* Do foo */
void foo();
/* Do bar */
void bar();
}
class A implements iFoo {
void foo() {};
void bar() {};
}
class B<iFoo> {
iFoo foo;
B() {
foo.foo();
foo.bar();
}
}
//somewhere in the code:
B b = new B<A>();
现在,如果我想实现一个可以用作B的类型参数的类C,我知道C必须实现iFoo。因此,我去那里,按照合同约定,所有必要的文件将在那里(我需要实现哪些方法,有什么签名加内联文档。
在C ++中,它看起来像这样(如果我错了,请纠正我):
class A {
public:
void foo();
void bar();
}
template<class T>
class B {
public:
T foo;
B() {
foo.foo();
foo.bar();
}
}
//somewhere in the code:
B *b = new B<A>();
哪里是记录B对T的期望的最佳位置?或者反过来说,如果我有A和B并希望实现一个作为类型参数传递给B的类C,我如何找出B对T的期望?上面当然是一个非常简单的例子,想象一下更大更复杂的类。
答案 0 :(得分:5)
在C ++中,纯抽象接口类看起来像这样:
struct IFoo
{
virtual void foo() = 0;
virtual void bar() = 0;
virtual ~IFoo() {}
};
然后你像普通类一样继承它
class A : public IFoo
{
public:
void foo();
void bar();
};
答案 1 :(得分:3)
我想说文档的好地方是定义类的头文件。您的库的用户将查看头文件以查看界面,这是她应该找到文档的位置,包括整个类或模板以及公共成员。
整个班级或模板以及每个公共成员都应该有详细说明目的,假设,先决条件等的文件。
示例:
<强> foo.h中:强>
// The "Foo" template implements a gizmo according to Smith, Jones et al.
// A Foo<T> can be used as a drop-in replacement for the usual Zip<T>.
//
// The template parameter T must be a complete object type which is
// assignable and copyable.
template <typename T>
class Foo
{
public:
// Reflect the template parameters
using type = T;
using refence = T &;
// Constructs a Foo with a given state of being active, a given age
// and an identifier that represents its glurgh.
Foo(bool active, int age, char id);
// Perform the magic act of foo.
// It is safe to call this function multiple times concurrently.
// Returns the number of bars.
//
// Intended usage:
//
// Foo x(false, 10, 'a');
// registerGlobally(x);
// x.activate();
// return x.perform();
//
int perform();
private:
// ...
};
理想情况下,如果没有其他参考资料,手册或教程,用户应该能够通过查看标题来确定如何使用该类。如果您遵守纪律并且并行维护适当的参考手册,您可以不使用示例,但必要的信息(参数的含义和要求,返回值的含义)应该在那里
当您从抽象基类公开继承时,这当然意味着基类已包含所有接口文档,并且您不需要重复一般信息。但是,您仍然应该在具体的覆盖范围内留下简短的评论,说明他们在实施方面的具体做法。
如果希望模板参数满足某些约束,例如从某个基类派生,则可以使用类型特征,SFINAE和模板元编程以编程方式强制执行。您仍应以人类可读的形式记录模板参数的要求,但在代码中强制执行此类约束是很好的卫生。
答案 2 :(得分:0)
您可以使用虚拟方法执行与C ++中的接口相同的操作,将=0
放在方法定义的末尾,您就拥有了有效的接口。 (它实际上是一个必须派生的基类,因为它本身无法实例化,在C ++术语中称为“abstract base class”。)。
但是这给了你答案,没有什么可以阻止你说A是基础界面并使用它作为'B期望T'的定义。因此,如果A有两个方法,foo()和bar(),那么它就是它提供的东西,这就是从A派生的所有类也都有的。
答案 3 :(得分:0)
你可以使用像Joachim建议的界面,但不幸的是,C ++中没有办法强制执行它,就像你在Java中那样。所以你必须把它记录为评论,比如函数或某个地方。