C ++中的术语“接口”


11

Java对class和进行了明确区分interface。(我相信C#也可以,但是我没有经验)。但是,在编写C ++时,类和接口之间没有语言强制的区分。

因此,我一直将接口视为解决Java中缺乏多重继承的一种解决方法。在C ++中做出这样的区分是任意的,毫无意义的。

我一直倾向于使用“以最明显的方式编写东西”的方法,因此,如果在C ++中,我得到了Java中所谓的接口,例如:

class Foo {
public:
  virtual void doStuff() = 0;
  ~Foo() = 0;
};

然后,我决定大多数的实现者Foo都想共享一些我可能会写的通用功能:

class Foo {
public:
  virtual void doStuff() = 0;
  ~Foo() {}
protected:
  // If it needs this to do its thing:
  int internalHelperThing(int);
  // Or if it doesn't need the this pointer:
  static int someOtherHelper(int);
};

然后,这使它不再是Java的接口。

相反,C ++具有两个重要概念,它们与相同的基础继承问题有关:

  1. virtual 继承力
  2. 没有成员变量的类用作基类时不会占用额外的空间

    “基类子对象的大小可能为零”

    参考

在我尝试尽可能避免的那些方法中,很少会遇到真正是“最清洁”设计的情况。#2是我对术语“接口”的理解与C ++语言功能之间的细微但重要的区别。因此,我目前(几乎)从来没有将事物称为C ++中的“接口”,而是谈论基类及其大小。我会说,在C ++的上下文中,“接口”是用词不当。

尽管没有多少人做出这样的区分,但引起了我的注意。

  1. 通过允许(例如protected)非virtual功能存在于C ++的“接口”中,我会失去任何东西吗?(我的感觉恰恰相反-共享代码的位置更自然)
  2. 术语“接口”在C ++中是否有意义-是仅表示纯文本virtual还是将没有成员变量的C ++类称为接口仍然公平?

C#具有与Java 相同的接口多重继承,实现的单一继承,但是通过使用通用扩展方法internalHelperThing可以在几乎所有情况下模拟C#。
Sjoerd 2012年

请注意,公开虚拟成员是一种不好的做法。
Klaim

~Foo() {}在几乎所有情况下,抽象类中的公众都是错误。

Answers:


11

在C ++中,术语“接口”不仅具有一个广泛接受的定义-因此,每当您要使用它时,您都应该确切地说出自己的意思-具有或不具有默认实现的虚拟基类,头文件,公共成员任意类等等。

关于您的示例:在Java(和C#中类似)中,您的代码可能暗示着关注点分离:

interface IFoo {/*  ... */} // here is your interface

class FooBase implements IFoo 
{
     // make default implementations for interface methods
}

class Foo extends FooBase
{
}

在C ++中,您可以执行此操作,但是您不必这样做。而且,如果您希望在没有成员变量但包含某些方法的默认实现的情况下将类称为接口,则可以这样做,但请确保与您交谈的每个人都知道您的意思。


3
对于C ++程序员,“接口”的另一个可能含义是文件的public各个部分.h
David Thornley,2012年

您的代码示例是哪种语言?如果这是尝试成为C ++的尝试,那么我将哭泣...
Qix-MONICA错误的

@Qix:保持简单,再次阅读我的文章(它清楚地表明该代码是Java的),如果您获得> 2000分,则可以编辑我的文章以添加等效的C ++示例代码,以使我的示例更多明确。
布朗

如果那是Java,那仍然是错误的,不,我不能编辑它。没有足够的字符可以更改。Java在实现/扩展中不使用冒号,这就是为什么我想知道这是否是C ++的尝试...
Qix-MONICA错误存在

@Qix:如果您发现更多语法问题,可以将其保留为圣诞节礼物:-)
Doc Brown

7

听起来好像您已经陷入使接口在概念上既是实现(接口-小写字母'i')又是抽象(接口-大写字母'I')含义混淆的陷阱)。

关于示例,您的第一部分代码仅仅是一个类。虽然您的类在某种意义上具有接口,因为它提供了允许对其行为进行访问的方法,但从接口声明的意义上说,它不是接口,它提供了抽象层来表示您可能希望类进行的行为类型实行。布朗博士(Doc Brown)对您的帖子的回答向您准确显示了我在这里所说的内容。

接口经常被吹捧为不支持多重继承的语言的“变通方法”,但是我觉得这更多的是误解,而不是硬道理(而且我怀疑我可能会因为说明而被激怒!)。接口实际上与多重继承无关,因为它们不需要继承或成为祖先就可以在类之间提供功能兼容性或实现抽象。实际上,如果您希望以这种方式实现代码,它们可以使您完全有效地放弃继承-不是我完全推荐这样做,而是说您可以做吧。因此现实是,无论继承如何,接口都提供了一种定义类类型的方法,建立了确定对象之间如何通信的规则,从而使您可以确定类应支持的行为而无需继承。规定用于实现该行为的特定方法。

通过允许(例如受保护的)非虚拟函数存在于C ++的“接口”中,我会失去任何东西吗?(我的感觉恰恰相反-共享代码的位置更自然)

纯接口的意思是完全抽象的,因为它允许定义类之间的兼容性契约,而类不一定是从公共祖先继承了它们的行为。实施后,您要选择是否允许在子类中扩展实施行为。如果您的方法不是virtual,则在您决定需要创建后代类时,您将失去扩展该行为的能力。不管实现是否是虚拟的,接口都定义了行为兼容性,而该类则为该类表示的实例提供了该行为的实现。

术语“接口”在C ++中是否有意义-是仅暗示纯虚函数,还是将没有成员变量的C ++类称为接口仍然公平?

原谅我,但是自从我真正用C ++编写了一个严肃的应用程序以来已经有很长时间了。我确实记得接口是出于抽象目的的关键字,正如我在此处描述的那样。我不会将任何类型的C ++类都称为接口,而是说该类具有我上面概述的含义内的接口。从这个意义上说,术语是有意义的,但这实际上取决于上下文。


1
+1表示“具有”和“存在”界面。
Doc Brown

6

Java接口不是“解决方法”,它们是有意的设计决策,它避免了诸如钻石继承之类的多重继承问题,并鼓励尽量减少耦合的设计实践。

带有受保护方法的接口是一个教科书,需要“优先考虑组合而不是继承”。要在Sutter和Alexandrescu的部分中引用其出色的C ++编码标准中的那个名称:

避免继承税:继承是C ++中第二紧密的耦合关系,仅次于友谊。紧密耦合是不希望的,应尽可能避免。因此,宁愿使用组合而不是继承,除非您知道继承确实对您的设计有所帮助。

通过在界面中包含辅助函数,您现在可能会节省一些键入,但是您引入的耦合将对您造成伤害。从长远来看,使您的助手功能分开并传递总是更好的选择Foo*

STL是一个很好的例子。尽可能多的辅助函数被引入,<algorithm>而不是放在容器类中。例如,由于sort()使用公共容器API进行工作,因此您知道可以实现自己的排序算法,而无需更改任何STL代码。这种设计使诸如boost的库能够增强STL而不是替换STL,而STL无需了解关于boost的任何知识。


2

通过允许(例如受保护的)非虚拟函数存在于C ++的“接口”中,我会失去任何东西吗?(我的感觉恰恰相反-共享代码的位置更自然)

您可能会认为,但是将受保护的非虚拟方法放在其他抽象类中将指示对编写子类的任何人的实现。这样做纯粹是违背了界面的目的,即提供隐藏其下面内容的贴面。

这是没有一个万能的答案,您必须利用自己的经验和判断来做出决定的情况之一。如果您可以100%确信地说本来是完全虚拟的类的每个可能的子类Foo都将始终需要protected方法的实现bar(),那么Foo它是正确的地方。一旦有了Baz不需要的子类bar(),您要么不得不接受Baz不应该访问的代码,要么进行重新安排类层次结构的练习。前者不是一个好习惯,而后者可能要花费比最初适当安排事情要花费的几分钟更多的时间。

术语“接口”在C ++中是否有意义-是仅暗示纯虚函数,还是将没有成员变量的C ++类称为接口仍然公平?

C ++标准的第10.4节顺便提及了使用抽象类来实现接口,但并未正式定义它们。该术语在一般的计算机科学环境中是有意义的,任何有能力的人都应该理解,“ Foo(是)接口”意味着某种抽象形式。那些接触过具有定义的接口构造的语言的人可能会认为是纯虚拟的,但是任何需要实际使用的人Foo都将在继续之前查看其定义。


2
提供纯虚拟声明和声明加实现之间有什么区别?在这两种情况下,都必须有一个实现,并且baz始终可以有一个类似的实现return false;。如果该方法不适用于所有子类,则它不以任何形式不属于基本抽象类。
David Thornley,2012年

1
我想你的最后一句话说我们在同一页上:没有理由在抽象类中没有受保护的方法,但是在继承树中它们不应比绝对必要更高。我只是认为,如果类必须虚拟实现,那么它不在树中的正确位置。
Blrfl 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.