什么样的多态在C ++中被认为更惯用?

时间:2010-06-30 14:13:50

标签: c++ programming-languages polymorphism idiomatic

C ++是一个value oriented language似乎并不支持OO(因此也就是子类型多态)。至于参数多态,缺乏对类型参数的类型推断和模板的详细语法使得它们难以使用。

请注意,我所熟知的唯一语言是Java(子类型多态)和Haskell(参数多态)。两种语言都倾向于一种多态性。但是C ++支持(在某种程度上),但两者似乎都在我发现不直观的问题上起作用。因此,当使用C ++进行编程时,我很难确定应该采用何种方式编写代码。

所以我的问题是在C ++中哪种多态被认为更惯用?

编辑1:

解释我的“C ++不支持OO”点:

动态方法调度和LSP在OO中很常见,不是吗?但是当谈到C ++时,不使用指针(原始或智能)来应用这些技术是不可能的(或实际的)。
例如,考虑使用Person方法virtual的类print将其名称输出到控制台。让另一个类Student扩展Person并覆盖print以打印他的名字和他的学校名称。

现在考虑以下功能:

void blah(const Person & p) {
  p.print();
}

如果我传递Student个对象,print方法会从print调用Person,而不是Student。因此,它违背了亚型多态性的基本思想。

现在我知道在这种情况下我可以使用动态分配(即指针)来实现子类型多态性。但是静态分配在C ++中更常见。指针用作最后的手段(我记得在这里读过其他一些帖子)。所以我觉得很难调和推荐静态分配而不是动态分配的好实践(这就是我说C ++是值时的意思具有亚型多态性的。)

使用Java时,我倾向于全部使用动态分配,因此子类型多态性在那里非常自然。但是,C ++并非如此,

希望我的观点现在清楚。

编辑2:

好的,我在编辑1中给出的例子是错误的。但我的观点仍然有效,我已多次面对这个问题。我无法回忆起我头脑中的所有情况。

这是我想到的一个案例。

在Java中,您可以在类中引用超类型,然后使它们指向其任何子类型的实例。

例如,

class A {
  B y1;
  B y2;
}

abstract class B {
  // yada yada
}

class B1 exyends B {
  // yada yada
}

class B2 extends B {
  // yada yada
}

此处y1中的引用y2A可以指向B1B2B的任何其他子类的实例1}}。无法重新分配C ++引用。所以我将不得不在这里使用指针。所以这证明在C ++中,不使用指针就不可能实现各种子类型多态性。

4 个答案:

答案 0 :(得分:12)

答案 1 :(得分:2)

通过创建模板化函数或对象的副本,在编译时评估模板。如果你在运行时需要多态(即:std::vector<Base*> vecvec->push_back(new Derived())时不时......)你被迫使用子类型和虚方法。

编辑:我想我应该提出模板更好的情况。模板是“开放的”,因为模板化的函数或对象将与您尚未创建的类一起工作......只要这些类适合您的界面。例如,auto_ptr&lt;&gt;适用于我可以制作的任何课程,即使标准图书馆设计师没有真正考虑过我的课程。类似地,模板化算法,例如反向支持任何支持解除引用的类,以及operator ++。

使用子类型时,必须在某处写下类层次结构。也就是说,你需要在你的代码中的某个地方说B extends A ...然后才能使用像A这样的B.另一方面,你不必说B implements Randomaccessiterator它就可以使用模板化代码。

在两种情况下,两者都满足您的要求,然后使用您更熟悉的使用。根据我的经验,这种情况不会经常发生。

答案 2 :(得分:2)

这两种选择都有其优点。 Java风格的继承在现实世界的C ++代码中更为常见。由于你的代码通常必须与其他人一起玩,我会首先关注多态性的分类,因为这是大多数人都熟悉的。

此外,您应该考虑多态是否真的是表达问题解决方案的正确方法。很多时候,人们在没有必要时会构建精心设计的继承树。

答案 3 :(得分:0)

'Suck'是一个非常强大的术语。也许我们需要考虑Stroustrup用C ++瞄准的目标

C ++的设计考虑了一些问题。在其他人中: 它必须向后兼容C. 它不应该限制程序员做他们想做的事情。 你不应该为你不使用的东西买单。

所以,第一个标准要坚持下去 - 在C ++中编译时,C中合法的一切都必须起作用(并且效果相同)。因此,包含了许多必要的妥协。 另一方面,C ++也为你提供了很多力量(或者,正如它已经为C描述的那样,“足以让自己挂起来。”)随着权力的到来,如果你选择的话,编译器不会与你争论做一些蠢事。

我现在承认,自从我上次查看Haskell以来已经过了大约15年,所以我对此有点生疏 - 但是参数化多态(完全类型安全)总是可以在C ++中被覆盖。 子类型多态可以。从根本上说,任何东西都可以被覆盖 - 如果你坚持将一个指针类型转换为另一个(无论多么疯狂),编译器都不会与你争论。

所以,尽管如此,C ++确实提供了许多带有多态性的选项。 经典的公共继承模型是'a' - 子分类。这很常见。

受保护的继承模型'是'实现'的'(继承实现,但不是接口)

私有继承模型'is-implemented-using'(包含实现)

后两者不太常见。聚合(在类中创建类实例)更为常见,而且通常更灵活。

但是C ++还支持多重继承(真正的实现和接口多重继承),具有所带来的重复继承的所有固有的复杂性和风险(可怕的钻石模式) - 以及处理它的方法。 (如果你感兴趣的话,Scott Myers的“有效的C ++”和“更有效的C ++”将有助于解决这些问题。)

我不相信C ++是一种排除其他东西的“价值导向语言”。 C ++可以是你想要的,非常多。你只需要知道你想要什么,以及如何做到这一点。 这并不是说C ++很糟糕,因为C ++非常敏锐,你可以轻松削减自己。