第8篇:C++ 类多继承(中篇)

前一篇介绍了C++继承的特征,我们本篇将介绍C++继承的分类

  • 单继承:在单一继承中,一个类只能从一个类继承。 即一个子类只能继承一个基类,Java就是这种继承类型。

  • 多继承:这是C ++面像对象的特性,一个子类可以从多个父类中继承属性和方法,这正是本篇的主题。它的语法模版如下所示

class 子类名称 :继承模式 父类名1,继承模式 父类名2,.....{
      //类代码体
} 

为了深入浅出,本篇拿我以前玩过的《最终幻想X-2》为例子,如果你玩过RPG游戏的话,你会知道下面的例子是多么地贴切适合本篇的主题.

但需要说明的是,这是仍然不太好的设计形式。我这里仅仅是用来说明C++多继承的特性。然而它不太符合现实中复杂的需求,我们本文最后会给出更符合复杂需求的方案。

一个角色,可以装配两个职业晶球-黑魔法师和白魔法师,那么角色Role就继承了上面BlackMagic类和WhiteMagic类的所有方法了。

ss17.png

多继承的示例
这是上面UML类图的代码实现

#include <string>
#include <iostream>

using namespace std;

class BlackMagic{
public:
  BlackMagic(){cout<<"初始化-黑魔法师"<<endl;}
  ~BlackMagic(){cout<<"内存回收-黑魔法师"<<endl;}
  size_t fira(){
    cout<<"fire初级火魔法"<<endl;
  }

  size_t thurder(){
    cout<<"初级雷魔法"<<endl;
  }

  size_t bio(){
    cout<<"毒属性魔法"<<endl;
  }
};


class WhiteMagic{
public:
  WhiteMagic(){cout<<"初始化-白魔法师"<<endl;}
  ~WhiteMagic(){cout<<"内存回收-白魔法师"<<endl;}
  size_t haste(){
    cout<<"haste魔法"<<endl;
  }

  size_t holy(){
    cout<<"圣属性魔法"<<endl;
  }

  size_t might(){
    cout<<"青魔法"<<endl;
  }
};

class Role:public WhiteMagic,public BlackMagic{
  string d_name;
  size_t d_hp;
  
public:
    Role(const string& name,
         size_t hp):d_name(name),d_hp(hp){
        cout<<"角色初始化 -"<<d_name
        <<"HP:"<<d_hp<<endl;
    }
    ~Role(){cout<<"内存回收-游戏角色"<<endl;}
    
    size_t attck(){
        cout<<d_name<<"施加attack方法"<<endl;
    }
    
    size_t magic(){
        cout<<d_name<<"施加magic方法"<<endl;
    }
};

int main(void){
    Role yuna=Role("Yuna",1500);
    
    yuna.fira();
    yuna.holy();
    return 0;
}

多继承的RAII约定

首先我们,从下面的输出结果可以看出子类在初始化过程

  • 首先按照子类实现的继承列表中的父类顺序执行初始化父类的构造函数
  • 然后,初始化子类本身的构造函数。

垃圾回收的过程即会继承列表中定义的父类顺序相反。

  • 首先,调用函数在结束之时隐式执行子类的解构函数。
  • 然后,依次逆序执行子类继承列表中父类的解构函数。


谨慎使用多继承

如果在实际项目中,你能够想到什么不足之处吗?如果你了解《最终幻想X-2》这个游戏的职业系统设定有些了解的,你体会到上面的代码上下文组织太过死板缺乏灵活性。FFX-2光是职业设定就有10多个,如果一个Role类要继承10个职业设定的其中2个,那么需要编写多少次上面示例代码组合?


你要编写继承组合需要那么多个囧.....

显然多继承无法适用于那些变数类型太大的情况,因此在你使用多继承进行组织代码之前,请在心中考虑这些问题。

  • 你设计的类中属性和方法的实现明确并且后期不会变动吗?
  • 你设计的类的作为多继承链中的父类,他们公开的接口不会有所变动吗?
  • 你在多继承链中混合使用各种继承模式(即public,protected,private等模式),子类中的属性和方法生成的最终的访问控制修饰符,你能够一目了然吗?

如果你自信心十足的话~恭喜你,小弟想拜你为师。

另外C++的多继承另外一个😈邪恶的一面,就是棱形问题,考虑如下这个UML类图存在什么问题?


ss_17.png

当一个类的两个父类具有共同的基类时,就会发生"棱形问题"。 例如,在上图的继承链中,Role类到Ability类,它获取了多少个属性副本?这是一个不合理的设计,根据上面分析的多继承RAII约定
Role类的分类初始化流程会如下顺序依次执行初始化:

  1. Ability() ➔Magic() ➔BlackMagic()
  2. Ability()➔Magic() ➔WhiteMagic()
  3. Ability()➔Physical()➔Warrior()

因此整个多继承链中Magic的构造函数执行了2次,Ability的构造函数执行了3次,因此Role构造整个过程中,获得了2份Magic实例中的属性副本,3份Ability实例中的属性副本。

这对于计算机资源管理角度考虑这是不可取的。所以对多继承没有深入认识的话,会对你的代码组织会造成各种负面的效果。

我们模拟一下上面分析过程的步骤1和步骤2上面,只需修改上文的示例代码,但注意这个是一个设计不良的示例。

class Ability{
public:
     Ability(){cout<<"初始化 - Ability实例"<<endl;}
     ~Ability(){cout<<"内存释放 -Ability实例"<<endl;}
};

class Magic:public Ability{
public:
     Magic(){cout<<"初始化 - Magic实例"<<endl;}
     ~Magic(){cout<<"内存释放 - Magic实例"<<endl;}
};

class WhiteMagic:public Magic{
   ....
};

class BlackMagic:public Magic{
  ....
};

最后输出的程序结果,跟我们以上分析的结果一样

多继承的可选方案

多继承就我个人而言,在局部的上下文中应用还是可以的,如果大规模使用的话,我更倾向于用其他流行的设计模式作为优选方案。而且在打算动手敲代码之前,不妨想本文示例中,用UML类图和工程流程图理清了你的思路之后再动手组织代码。上面的示例代码,我们可以使用常见的组合模式来组织代码,比如N个职业设定,我们都单继承于一个Career的基类,并且我们向Role类的构造函数传递2个Career类,这种组织代码的模式更为灵活,能够适用绝大多数场合。

class Career{...};

class BlackMagic: public Career{...};
....
class WhiteMagic:public Career{...};
....

class Role{
public:
     Role(Career ca1,Career ca2,string name,...)
     {...}
};

后记

我们最后一篇会提到多继承中,子类如何调用父类指定构造函数的问题

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容