前言
相对而言了解了继承在构造函数和析构函数时执行的区别。
包括函数重命名时作用域区分的时候。
正文
多重继承
从生理学角度,一个儿子就一个爹。
但是子类比较特殊,它可以拥有多个父类
1 2 3 4 5 6 7 8 9 10 11 12 13
| class pople{ public: pople(){ std::cout << "pople created\n"; } };
class wolf{ public: wolf(){ std::cout << "wolf created\n"; } };
|
当狼和人组合,产生子类狼人
1 2 3 4 5 6
| class werewolf:public pople,public wolf{ public: werewolf(){ std::cout << "werewolf created\n"; } };
|
继承方式无所谓,一般都public,父类名由逗号分隔。
实例化类,能看到他是继承了两个父类,构造函数的顺序也跟我们继承时候的顺序相关。
当两个父类有相同的成员函数时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class pople{ public: pople(){ std::cout << "pople created\n"; } void eat(){ std::cout << "pople eat!\n"; } };
class wolf{ public: wolf(){ std::cout << "wolf created\n"; } void eat(){ std::cout << "wolf eat!\n"; } };
|
用父类作用域的函数是最直接的行为。
另一种就是uisng去强制解锁。但是面对重名的情况感觉还是靠作用域比较好。
不过归根到底,展现给用户的肯定是越简单越好。
那么究竟是处于哪个父类的状态时就调用哪个父类的功能,比如加个if,条件可以自己想,狼人,要么怒气拉满,要么月圆之夜什么的。
1 2 3 4 5 6 7 8 9 10 11 12
| class werewolf:public pople,public wolf{ public: werewolf(){ } void eat(){ if (anger == 100) wolf::eat(); else pople::eat(); } private: unsigned short anger = 0; };
|
当怒气为0的时候,还处于人类状态,行为自然就是人类的行为这很好理解。
修改怒气为100的时候,达到了变身条件,处于狼的状态,行为转化为狼的行为
1
| unsigned short anger = 100;
|
至于怒气怎么增加,就看想要什么样的感觉设定了,这就不讨论了。
多重继承重复
werewolf是继承自wolf和pople。倘若wolf和pople也有父类。
那么werewolf就会出现重复的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| class animal{ public: int x; int y; };
class pople:public animal{ public: pople(){ } void eat(){ std::cout << "pople eat!\n"; } };
class wolf:public animal{ public: wolf(){ } void eat(){ std::cout << "wolf eat!\n"; } };
class werewolf:public pople,public wolf{ public: werewolf(){ } void eat(){ if (anger == 100) wolf::eat(); else pople::eat(); } private: unsigned short anger = 100; };
|
我们创建新的类animal,让wolf和pople去继承。
并且animal类中有两个成员变量x,y。
当我们给werewolf实例化对象的x和y赋值的时候就能看到问题了:
编译器提示到x不明确,为什么会不明确
随便画个草图示意。就是因为多重继承的时候,父类的父类,套娃了。重复继承了xy属性。
解决这种情况,肯定是作用域最直接,直接告诉他指向哪个区域就行了。
而且多重继承带来的问题就是内存,animal的属性被wolf和pople分别继承之后,又共同被werewolf继承,这样一来,werewolf就会有多组的x和y占用内存。
还有一种方法就是虚基类,也是跟后面虚函数有点关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| class animal{ public: int x; int y; };
class pople:public virtual animal{ public: pople(){ } void eat(){ std::cout << "pople eat!\n"; } };
class wolf:public virtual animal{ public: wolf(){ } void eat(){ std::cout << "wolf eat!\n"; } };
class werewolf:public pople,public wolf{ public: werewolf(){ } void eat(){ if (anger == 100) wolf::eat(); else pople::eat(); } private: unsigned short anger = 100; };
|
通过两组虚继承之后,测试之前的样例:
看到wolf的xy居然都变成了pople的xy设置的值,这也是virtual
的好处,他会判断是否重复引用了,当子类重复继承到父类的属性的时候,就会主动忽略掉。
结语
只不过多重继承带来的组合问题,有些时候不利于解读,毕竟从小零件构造大物件,还是把大物件拆成小零件。