【发布时间】:2009-07-07 18:37:37
【问题描述】:
要清楚,我不是在问多重继承是好是坏。我从那场辩论的双方那里听到了很多争论。
我想知道在 C++ 中是否存在任何类型的设计问题或场景,其中多重继承要么是完成某事的唯一方式,要么至少是所有其他替代方案的最佳方式,以至于它不会考虑其他任何事情都没有意义。
显然,这个问题不适用于不支持多重继承的语言。
【问题讨论】:
要清楚,我不是在问多重继承是好是坏。我从那场辩论的双方那里听到了很多争论。
我想知道在 C++ 中是否存在任何类型的设计问题或场景,其中多重继承要么是完成某事的唯一方式,要么至少是所有其他替代方案的最佳方式,以至于它不会考虑其他任何事情都没有意义。
显然,这个问题不适用于不支持多重继承的语言。
【问题讨论】:
policy-based design 不能没有多重继承。因此,如果基于策略的设计是解决您的问题的最优雅的方式,那么这意味着您需要多重继承来解决您的问题,而不是所有其他选项。
如果不滥用多重继承(就像一切,任何语言),多重继承会非常有用。
【讨论】:
在这种情况下,您会从一个类继承,并可能在 Java 中实现一个或两个接口。我认为这是您可以通过 C++ 中的多重继承来解决的问题。
【讨论】:
C++ 流使用多重继承:istream 和 ostream 都是 iostream 的父级。因为它们都继承自ios_base,所以你有一颗钻石。
这是唯一“合理”的解决方案,因为标准库的流部分采用与算法和集合相同的路线是不合理的。所以 ostream 的行为是多态的,而不是像 Iterator(*) 这样的“鸭子类型”接口。
一旦有了动态多态性,就需要多重继承来同时实现多个接口。
(*) 大概这是因为其他任何事情都会一团糟。您必须能够编写操作流的实际函数,而不是强迫用户到处都有模板。这是因为写“一些流,直到运行时我才知道什么”是很常见的,但不想操作“一些集合,直到运行时我才知道什么”。
【讨论】:
如果您需要继承行为,而不仅仅是契约,多重继承很有用。然而,正如其他语言所展示的那样,多重继承并不是解决该问题的唯一方法,其代价是使继承树更深。因此,您必须且只能使用多重继承的情况非常少见。
【讨论】:
我已经阅读了 Java 接口等内容,以便更好地了解这个问题的答案。接口背后的想法是创建一个抽象类,作为另一个类的模板。这里的优点是模板可以在一个具体的类中组合。例如-
父类- FoodStore 子类-CoffeeShop 子类-面包店
使用此继承树,FoodStore 可以是 Bakery 或 CoffeeShop,但不能同时是两者。但那我们怎么称呼星巴克呢?
更好的方法,IMO-
父类- FoodStore 界面-CoffeeShop 界面-面包店
公共类 Starbucks 扩展 FoodStore 实现 CoffeeShop、Bakery { ... }
您必须了解一点 Java 才能理解这一点,但必须掌握它。接口相当初级,IMO。
作为进一步的思考,也许接口的设计是为了遵守“不要重复自己”。很明显,既然我提到了它。
【讨论】:
当您想要继承功能而不是角色时,例如 boost::noncopyable(支持此功能的其他语言(与 Java 和 C# 不同)将其称为 mixin)。
【讨论】:
正如其他答案所说:
使用纯虚拟基类作为“接口”,就像在 Java (http://en.wikipedia.org/wiki/Interface_(Java)) 中一样,这是一个非常常见的 O.O.所有 O.O. 中的模式语言,不仅是 Java
做基于警察的设计
还有:
【讨论】:
当您必须组合两个或多个第三方类层次结构时,每个层次结构都要求对象从层次结构自己的基类派生,那么缺少多重继承将使您的代码复杂而繁琐。
namespace Object_Database {
class Object {
public:
virtual void store() ;
virtual void fetch() ;
};
}
namespace Reflectives {
class Object {
public:
virtual std::vector<std::string> > membernames();
virtual std::vector<std::string> > methodnames();
};
}
第一个层次结构允许用户创建可以序列化到对象数据库和从对象数据库中序列化的对象,并要求所有此类对象都派生自 Object_Database::Object 类。第二个层次结构允许用户创建可以在运行时查询其成员名称的对象,并要求所有此类对象都派生自 Reflectives::Object。
如果你需要可以同时做这两件事的对象,你只需要写:
class ReflectivePickle :
public Object_Database::Object,
public Reflectives::Object {
// ...
};
其他解决方案不合理。
【讨论】:
当基类是“接口类”时,我倾向于在 C++ 中使用多重继承,即所有方法都是纯虚拟的基类,没有一个实现 [记住你仍然可以定义一个实现,但你必须显式调用它],并且没有数据成员。非常类似于 Java 或(据我所知)C# 中的“接口”。
要在 C++ 中使用多态,不能使用组合,必须使用(公共)继承。
因此,如果 Bar 类(公开)继承自 Printable 和 Serializable,我可以将对象视为可打印对象、可序列化对象或 Bar 对象(使用指针或引用)。
对于作曲,你无法做到这一点。
【讨论】:
如果您想查看多继承的漂亮实现,请查看 Eiffel。他们通过特征重命名解决了菱形问题,比范围解析简单得多,它甚至支持直接重复继承,例如:
A继承B、B、B
当需要使用这种类型的继承时。
他们的内核库是开源的,如果您想查看示例,则广泛使用多重继承。
【讨论】: