C++ 如果使用多个is-a继承,您是否曾经不想使用共同基类的虚拟继承?
如果您有使用公共继承实现的is-a继承关系,并且有一个继承菱形,那么您将有如下结果:C++ 如果使用多个is-a继承,您是否曾经不想使用共同基类的虚拟继承?,c++,oop,inheritance,C++,Oop,Inheritance,如果您有使用公共继承实现的is-a继承关系,并且有一个继承菱形,那么您将有如下结果: 小班 从流派生的输入流和输出流类 从两者派生的输入/输出流类 在这种情况下,正如在标准库(?)中使用的那样,如果iostream同时是一个istream和一个ostream,istream isa-a流和ostream都是一个流,而且它们是同一个流,那么流中的任何函数,应用于iostream都应该处理相同的底层结构 在C++中,为了使IsReAM和OFSUW中的流副本能够共享,必须由它们继承。 但是,如果愿
- 小班
- 从流派生的输入流和输出流类
- 从两者派生的输入/输出流类
我的问题是,[编辑:是否有其他情况]除了“这不是一个真正的is-a关系,我顽皮地使用公共继承作为语法上的便利,当它在概念上是无效的”或“我从不需要从最派生的类中多态地引用基类,所以难以置信的小开销是不值得的”,从概念上讲,非虚拟继承并拥有基类的两个副本是有效的,每个姐妹中间类一个副本?问题在于身份;就iostream而言 在层次结构中,共享基甚至有状态,由 操纵者。如果存在多个 基本ios,您将有两个状态副本(格式化 标志、错误状态,甚至streambuf),这将是 一场灾难 我真的想不出你会想要两份 一个基类,但我认为它们确实存在。一个例子
class Category
{
public:
virtual std::string CatName();
};
class OxygenBreath : public Category
{
public:
virtual void Breath() = 0;
std::string CatName(){ return "OxygenBreath";}
};
class LandWalk : public Category
{
public:
virtual void Walk() = 0;
std::string CatName(){ return "LandWalk";}
};
class Human : public OxygenBreath, public LandWalk
{};
我想说,这更有可能造成大破坏 让我们忽略有状态基类的情况。显然,有两个非相干态至少可能会引起混淆,并且容易出错 我宁愿把重点放在身份问题上。在C++中,对象的标识由其地址决定。这就是为什么除了空基类之外,每个对象的大小必须至少为一个字节的原因 如果在层次结构中多次使用相同的基类,则可以获得两个引用相同对象的
base*
。。。但不同(指向不同的地址)
当然,您可以使用
dynamic\u cast(p)
来获取整个对象的“真实”物理地址。。。但仍然如此。在某些情况下,这可能是有用的。假设一个触摸屏
类来自显示设备
和输入设备
?现在假设公共基类的每个基类都有一个估计功耗字段,那么避免虚拟继承不是很有用吗?从理论上讲,我可以想象一个非虚拟私有地从公共a继承“姐妹”B和C,然后从a和B公开继承某个“复合类”D的理由
当然,它不是大多数派生类(D)和大多数基类(a)之间的关系,因为私有继承不会表现出与用户及其后代的关系
但是,如果B和C姐妹类不是专门为这种情况设计的(即菱形继承),它们可能是从A公开继承的
其效果与私有继承几乎相同:我们无法从D访问成员([编辑:至少在没有显式强制转换到姐妹的情况下;]任何尝试都将是不明确的)。
因此,我们可以将公共非虚拟继承视为私有非虚拟继承的替代品
但我决不会做这样的把戏。
Iheritance本身是一个有用的隐喻,但不是宇宙的基础,使用它的细微特征会导致不必要的复杂设计
在我看来,如果用虚拟继承替换简单(非虚拟)继承(概念上)是不可接受的,那么几乎肯定存在设计缺陷
所以我会回答“不”(尽管我的“理论上讲……”结构:)
现在,考虑一下:
void do_some_duty(const employee& e)
{
e.do_some_tasks();
e.report("done");
}
void face_disaster(const human& h)
{
h.report("Oh my!");
}
void commit_suicide(const managed& m)
{
m.report("I want to suicide");
}
作为一名人类员工,如果您的工作地点不是:
human_employee h;
if (h.at_work()) commit_suicide(static_cast<const employee&>(h));
else commit_suicide(static_cast<const human&>(h));
human_员工h;
如果(h.at_work())自杀(static_cast(h));
否则自杀(静态施法(h));
如果我真的需要的话,我甚至会考虑使用这样的设计。您可以想象,代替managed
的是某个类,该类将存储对全局管理器对象的引用,例如垃圾收集。在这种情况下,为基本对象提供不同的基类是很有意义的。让我们来研究一个简单的示例
B B1 B2
| \ /
D D
在左边,我们找到了一个从低音中派生出来的类。在OO设计中,考虑到Liskov替换原则,具有is-a关系显然是合理的
在右侧,B1
和B2
都是D
的独立基础,可以使用B1*
或B2*
(或参考文献)多态访问D
。希望我们能够接受这也是一个有效的OO设计(尽管Sun认为这对Java程序员来说太难了;-P)。然后考虑从代码< B1 < /代码>和<代码> B2 < /代码>中得到的一些基本功能,并使它们可重用:假设它们都在任意数量的列表上操作,它们可以合理地授予直接/代码>公共<代码>访问:
Container<int> Container<int>
\ /
B1 B2
\ /
D
容器
\ /
B1 B2
\ /
D
如果尚未单击,可能:
Container<int> Container<int>
\ /
Ages Heights
\ /
Population
容器
\ /
Container<int> Container<int>
\ /
Ages Heights
\ /
Population