多重继承
前面我们介绍的派生类只有一个基类,称为单基派生或单一继承。在实际运用中,我们经常需要派生类同时具有多个基类,这种方法称为多基派生或多重继承。
2.1 多重继承的声明:
在 C++ 中,声明具有两个以上基类的派生类与声明单基派生类的形式类似,只需将要继承的多个基类用逗号分开即可。
在多重继承中,公有派生和私有派生对于基类成员在派生类的可访问性与单继承的规则相同。
另外,对基类成员的访问必须是无二义的,若两个基类中具有同名的数据成员或成员函数,使用成员名限定来消除二义性,若派生类中新增成员或成员函数与基类成员或成员函数同名,则派生类会覆盖外层同名成员,也须使用作用域分辨符。
2.2 多重继承的构造函数和析构函数:
多重继承的构造函数的定义形式与单继承构造函数的定义形式类似,只有 n 个基类的构造函数之间用“,”分隔。
多重继承的构造函数的执行顺序与单继承构造函数的执行顺序相同,也是遵循先执行基类的构造函数,再执行对象成员的构造函数,最后执行派生类构造函数的原则。在多个基类之间,则严格按照派生类声明是从左到右的顺序来排列先后。而析构函数的执行顺序与构造函数的执行顺序相反。
2.3 虚基类 :
如果某个派生类的部分或全部直接基类是从另一个共同的基类派生而来,在这些基类中,从上一级基类继承来的成员就有相同的名称,则在这个派生类中访问这个共同的基类中的成员时,可能会产生二义性,此时,可定义虚基类。这就要求在其直接基类的定义中,使用关键字 virtual 将那个共同的基类定义为虚基类,其语法形式如下:
class 派生类名: virtual 派生方式 基类
虚基类的初始化与一般的多重继承的初始化在语法上是一样的 ,但构造函数的调用顺序不同,虚基类构造函数的调用顺序是这样规定的:
1) 在同一层次中,先调用虚基类的构造函数,接下来依次是非虚基类的构造函数,对象成员的构造函数,派生类的构造函数。
2) 若同一层次中包含多个虚基类,这些虚基类的构造函数按对他们说明的先后次序调用
3) 若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再调用派生类构造函数。
上面是理论,有点儿晦涩,下面举一个例子.以下图为例:
接口Common是所有类的公共接口,Server接口继承Common接口,Client接口继承Common接口,CommonImpl是Common接口的一个公共实现。ServerImpl是Server接口的实现,它的Common接口的实现直接使用CommonImpl类的实现。
如果在C#中,这个问题会自然而优雅的解决,即编译器会知道,如果1个类继承了Server接口,又继承了CommonImpl实现,那么ServerImpl只需要实现Server接口中特定的方法即可。唯一的限制是CommonImpl必须放在Server接口的前面。看下面的演示代码:
using System;
using System.Collections.Generic;
using System.Text;namespace Demo
{interface IA{string funcInA();}interface IB : IA{string funcInB();}public class CA : IA{#region IA 成员public string funcInA(){return "CA.funcInA";}#endregion}public class CB : CA, IB{#region IB 成员public string funcInB(){return "CB.funcInB";}#endregion}}
类CB只需要实现接口IB中的方法即可。CA,IB必须以这个顺序出现;
对于C++而言,编译器是不知道的,如果你希望所有子类共享一个基类,或者子类都共用一个通用实现的基类,那么你需要把共享的那部分,声明为virutal基类,上图中的例子,就必须把Common的子类继承,声明为虚基类继承。
#pragma once
#include <iostream>
using namespace std;class IA
{
public:virtual int FuncInA() =0;
};class IB:virtual public IA
{
public:virtual int FuncInB()=0;
};class CA:virtual public IA
{
public:virtual int FuncInA(){cout<<" CA::FuncInA "<<endl;return 1;};
};class CB:public CA,public IB
{
public:int FuncInB(){cout<<"CB::FuncInB"<<endl;return 2;};/*virtual int FuncInA()
{
cout<<"CB::FuncInA"<<" Call CA::FuncInA() ==>"<<CA::FuncInA()<<endl;
return 3;
};*/};
上面的代码编译可以通过,且结果如期望。如果去掉virtual继承修饰符,就会报错。