c++ 钻石继承

在C++中,什么叫做钻石问题(也可以叫菱形继承问题),怎么避免它?

下面的图表可以用来解释钻石问题。

假设我们有类B和类C,它们都继承了相同的类A。另外我们还有类D,类D通过多重继承机制继承了类B和类C。因为上述图表的形状类似于钻石(或者菱形),因此这个问题被形象地称为钻石问题(菱形继承问题)。现在,我们将上面的图表翻译成具体的代码:

[cpp] view plaincopy

  1. /*
  2. Animal类对应于图表的类A
  3. */
  4. class Animal { /* ... */ }; // 基类
  5. {
  6. int weight;
  7. public:
  8. int getWeight() { return weight;};
  9. };
  10. class Tiger : public Animal { /* ... */ };
  11. class Lion : public Animal { /* ... */ }
  12. class Liger : public Tiger, public Lion { /* ... */ };

在上面的代码中,我们给出了一个具体的钻石问题例子。Animal类对应于最顶层类(图表中的A),Tiger和Lion分别对应于图表的B和C,Liger类(狮虎兽,即老虎和狮子的杂交种)对应于D。

现在,问题是如果我们有这种继承结构会出现什么样的问题。

看看下面的代码后再来回答问题吧。

[cpp] view plaincopy

  1. int main( )
  2. {
  3. Liger lg ;
  4. /*编译错误,下面的代码不会被任何C++编译器通过 */
  5. int weight = lg.getWeight();

在我们的继承结构中,我们可以看出Tiger和Lion类都继承自Animal基类。所以问题是:因为Liger多重继承了Tiger和Lion类,因此Liger类会有两份Animal类的成员(数据和方法),Liger对象"lg"会包含Animal基类的两个子对象。

所以,你会问Liger对象有两个Animal基类的子对象会出现什么问题?再看看上面的代码-调用"lg.getWeight()"将会导致一个编译错误。这是因为编译器并不知道是调用Tiger类的getWeight()还是调用Lion类的getWeight()。所以,调用getWeight方法是不明确的,因此不能通过编译。

 

钻石问题的解决方案:

我们给出了钻石问题的解释,但是现在我们要给出一个钻石问题的解决方案。如果Lion类和Tiger类在分别继承Animal类时都用virtual来标注,对于每一个Liger对象,C++会保证只有一个Animal类的子对象会被创建。看看下面的代码:

[cpp] view plaincopy

  1. class Tiger : virtual public Animal { /* ... */ };
  2. class Lion : virtual public Animal { /* ... */ }

你可以看出唯一的变化就是我们在类Tiger和类Lion的声明中增加了"virtual"关键字。现在类Liger对象将会只有一个Animal子对象,下面的代码编译正常:

[cpp] view plaincopy

  1. int main( )
  2. {
  3. Liger lg ;
  4. /*既然我们已经在Tiger和Lion类的定义中声明了"virtual"关键字,于是下面的代码编译OK */
  5. int weight = lg.getWeight();
  6. }

因为Java不支持多继承,所以不会出现菱形继承问题。但是Java可以通过接口间接实现多重继承。

[java] view plaincopy

    1. Class Mule implements Horse,Donkey
    2. {
    3. /* Horse和Donkey是接口*/
    4. }
时间: 2024-12-26 14:10:29

c++ 钻石继承的相关文章

深入super,看Python如何解决钻石继承难题

1.   Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 class Base(object): def __init__(self): print “Base init” 则普通方法如下 class Leaf(Base): def __init__(self): Base.__init__(self) print “Leaf init” super方法如下 class Leaf(Base): def __init_

(转载)深入super,看Python如何解决钻石继承难题

1.   Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 class Base(object): def __init__(self): print "Base init" 则普通方法如下 class Leaf(Base): def __init__(self): Base.__init__(self) print "Leaf init" super方法如下 class Leaf(Bas

继承的爱恨情仇——一场钻石引发的血案

最近在看PHP手册,发现了一个稀奇古怪的新玩意——trait. 这引起了我极大的兴趣,由于PHP面向对象的部分有很大程度和Java类似,我就自觉不自觉地和Java对比着来看. 这又让我想起了那个古老的故事——单继承和多继承. 说到这个问题,我最先想起的应该是C++和Java 图片来源:编程语言拟人化(1):Java.C++.Python.Ruby.PHP.C#.JS趣文:编程语言拟人化(第二弹)這系列文章的原始出處,是日本的「リクナビ NEXT」這家日本的人力公司的<Java.C++.Pytho

day24 继承 接口 多态

抽象类与接口类 接口类 继承有两种用途: 一:继承基类的方法,并且做出自己的改变或者扩展(代码重用) 二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能 class Alipay: ''' 支付宝支付 ''' def pay(self,money): print('支付宝支付了%s元'%money) class Applepay: ''' apple pay支付 ''' def pay(

Python全栈之路系列----之-----面向对象4接口与抽象,多继承与多态)

接口类与抽像类 在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念 编程思想 归一化设计: 1.接口类 不实现具体的方法,并且可以多继承 2.抽象类 可以做一些基础实现,并且不推荐多继承 编程的几类原则: 开放封闭原则:对扩展示开放的,对修改是封闭的依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该应该依赖细节:细节应该依赖抽象.换言之,要针对接口编程,而不是针对实现编程接口隔离原则:使用多个专门的接口,而不使用单一的总接口.

038类和对象:继承

继承1.继承简单使用:   格式:class DerivefClassName(BaseClassName):         BaseClassName:为基类.父类或超类         DerriverfClassName:为子类   如:>>> class Parent:             #父类       ...     def hello(self):       ...         print("father")       ...     

C++_基础_继承、多态

内容: (1)子类中的拷贝构造和拷贝赋值 (2)多继承和虚继承 (3)多态的初识 (4)虚析构的特性和使用 (5)多态的底层实现 (6)纯虚函数.抽象类的概念 1.子类中的拷贝构造和拷贝赋值 子类中的拷贝构造和拷贝赋值可能也需要显式的指定父类子对象的拷贝构造和拷贝赋值的方式 2.多继承和虚继承2.1 格式: class 子类名 : 继承方式 父类名1,继承方式 父类名2,... { //子类中的属性和行为 }; 如: 员工类 / \销售员工类 管理者类 \ / 销售管理类 叫做:钻石继承 2.2

面向对象的进阶-------继承

初始面向对象之后,我们来学习面向对象的三大特性以及面向对象的其他补充方面的知识.   继承         定义:继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又叫做基类或超类,新建的类被称为派生类或子类.      python中类的继承可以分为单继承和多继承 查看继承的方法是_base_ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类 注:如果没有指定基类,python中的类会默认继承object类,object类

【C++】浅谈三大特性之一继承(三)

四,派生类的六个默认成员函数 在继承关系里,如果我们没有显示的定义这六个成员函数,则编译系统会在适合场合为我们自动合成. 继承关系中构造函数和析构函数的调用顺序: class B { public: B() { cout<<"B()"<<endl; } ~B() { cout<<"~B()"<<endl; } }; class D:public B { public: D() { cout<<"D