C ++和Java中的抽象类/接口是否有不同的用法依据


13

根据Herb Sutter的说法,应该更喜欢抽象接口(所有纯虚函数)而不是C ++中的抽象类,以尽可能地实现分离。虽然我个人认为该规则非常有用,但是最近我加入了一个由许多Java程序员组成的团队,并且在Java代码中似乎没有该准则。函数及其实现通常位于抽象类中。因此,即使对于C ++,我也将Herb Sutter弄错了吗?还是与Java相比,C ++中抽象函数的用法是否存在一般差异?在Java中,带有实现代码的抽象类比在C ++中更明智吗?如果是,为什么呢?


1
我有些怀疑,最后把它放在这里,因为它可能是由于我对Java oo缺少的一些设计原则造成的。因此,这不是一般性建议,而是更多关于使用该语言的对与错
Martin

接口应该是纯虚拟的。抽象类的想法是它们是部分实现的,并且可以由实现来填补空白而不必不必要地重复代码(例如,为什么可以在每个子类中都具有write(byte)和write(int)),这取决于实现来自write(int)的抽象类调用write(byte))

1
可能相关:stackoverflow.com/q/1231985/484230提供了Java中偏爱抽象类的原因。对于C ++这个原因似乎并没有真正由于坚持自由功能所有脑干,可以在接口级别添加功能
马丁

1
我认为黄金法则是“使非叶类抽象化”,但这并没有提出任何“仅纯”或“空”的要求。
Kerrek SB 2012年

1
如果对您有用,那么对您也有用。我真的不明白为什么人们一旦他们的代码不再遵守最新意见会感到恐慌。
詹姆斯

Answers:


5

OOP具有组成和替代。

C ++具有多重继承,模板专门化,嵌入和值/移动/指针语义。

Java具有单一继承和接口,嵌入和引用语义。

OOP学校使用这些语言的常见方式是采用继承进行对象替换,并嵌入进行组合。但是,您还需要一个共同的祖先和一种运行时广播的方式(在C ++中称为dynamic_cast,在Java中只是从另一个接口请求接口)。

Java通过其自己的java.lang.Object扎根层次结构来完成所有这些工作。C ++没有预定义的公共根,因此您至少应对其进行定义以产生相同的“图片”(但这限制了某些C ++的可能性...)。

此后,具有编译时多态性(考虑CRTP)和值语义的可能性还可以为“ OOP对象”的概念可以移植到C ++程序中提供其他选择。

您甚至可以想象,使用嵌入和隐式转换来管理替代品和使用私有继承来管理合成的异端,实际上是在颠覆传统的学校范式。(当然,这种方式比其他方式要年轻20岁,因此不要指望这样做会得到广泛的社区支持)

或者,您可以想象所有类的虚拟公共基础,从接口(无实现)到最终类(完全实现),通过部分实现的接口甚至是均匀的接口簇,使用“优势”作为通过“多堆叠”从接口到实现的分派-“平行四边形”继承方案。

假设只有一种,并且只有一种OOP方式会限制两种语言的功能,因此将OOP与Java与C ++进行比较。

强迫C ++严格遵守Java编码习惯会使C ++变性,因为迫使Java像类C ++语言一样行为使Java变性。

这不是两种语言所具有的“敏感性”问题,而是两种语言所具有的不同“聚合机制”,以及将它们组合在一起的不同方式,这使得某些成语在一种语言中比另一种语言更有利可图,反之亦然。


1
我认为这个答案非常有趣,因为它简洁地将语言功能描述为面向对象的工具和设计原则,只是作为一种帮助而不是一种学说。但是,如果要在c ++中执行oo,则不需要公用根。这完全是错误的,因为您拥有运算符和模板(正如您还指出的,这是Java主树设计的非常强大的替代品)。除此之外,您的观点是所有答案中最有价值的
马丁

1
@马丁:在“技术意义上的”你是对的,但如果你需要运行时polymorophism(因为实例化对象的实际类型依赖于程序输入)一个 “根”(“a”是一个文章,而不是快捷方式“唯一”)是使所有对象“表亲”和层次结构在运行时可移动的原因。不同的根源起源于彼此不相关的不同祖先。这是“好”还是“坏”是背景问题,而不是习语。
Emilio Garavaglia

确实如此。我以为您指的是为整个C ++程序人为地提供一个通用根,并将其视为与Java相比不存在的缺陷。但是,在您进行编辑之后,您可以很清楚地说明这一点。再次感谢
马丁

12

两种语言都适用该原则,但是您没有进行公平的比较。您应该将C ++纯抽象类与Java接口进行比较。

即使在C ++中,您也可以具有实现了某些功能的抽象类,但是这些抽象类是从纯抽象类派生的(没有实现)。在Java中,您将具有相同的抽象类(具有某些实现),这些抽象类可以从接口派生(没有实现)。


因此,什么时候比C ++中的接口类更喜欢抽象类。我总是选择在C ++中使用接口以及非成员函数。
马丁

1
@Martin取决于设计。基本上,总是喜欢使用接口。但是“ 永远 ”的规则都有例外……
Luchian Grigore 2012年

确实如此,但是在Java代码中,我看到抽象类在很大程度上代表了大多数。这可能是由于在Java中无法在接口上使用自由函数的事实吗?
马丁

3
@Martin良好的自由函数在Java中根本不可能,因此这可能是一个原因,是的。好地方!回答您自己的问题!您可以自己添加一个答案,我想就是这样。
Luchian Grigore 2012年

4

通常,相同的面向对象原则适用于Java和C ++。但是,一个很大的不同是C ++支持多重继承,而在Java中,您只能从一个类继承。这就是我认为Java具有接口的主要原因,以补充缺乏多重继承并可能限制您可以使用它的功能(因为对滥用多重继承提出了很多批评)。因此,可能在Java程序员的脑海中,抽象类和接口之间存在更强的区别。抽象类用于共享和继承行为,而接口仅用于添加额外的功能。请记住,在Java中,您只能从一个类继承,但是可以有许多接口。但是,在C ++中,纯抽象类(即“ C ++接口”) 用于共享和继承行为,与Java接口的目的不同(尽管仍然需要实现这些功能),因此用法与Java接口不同。


0

有时,有一些默认的实现很有意义。例如,适用于所有子类的通用PrintError(string msg)方法。

virtual PrintError(string msg) { cout << msg; }

如果确实需要,它仍然可以被覆盖,但是您可以通过允许客户端只调用通用版本来为客户端节省一些麻烦。

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.