Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如果使用多个is-a继承,您是否曾经不想使用共同基类的虚拟继承?_C++_Oop_Inheritance - Fatal编程技术网

C++ 如果使用多个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同时是一个istream和一个ostream,istream isa-a流和ostream都是一个流,而且它们是同一个流,那么流中的任何函数,应用于iostream都应该处理相同的底层结构

在C++中,为了使IsReAM和OFSUW中的流副本能够共享,必须由它们继承。 但是,如果愿意,您不能虚拟继承,每次引用基类的成员时,请指定您想要的两个副本中的哪一个(一个在istream中,另一个在ostream中)(通过强制转换或使用scope::blah)


我的问题是,[编辑:是否有其他情况]除了“这不是一个真正的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