C++:多重继承
4.1 多重继承
单继承或单基派生:派生类只有一个基类;
多继承或多基派生:派生类有两个或两个以上的基类。
4.4.1 多重继承派生类的声明
在C++中,声明具有两个以上的派生类与声明单机派生类的形式相似,只需将要继承的多个基类用逗号分分隔即可,其声明的一般形式如下:
class 派生类名:继承方式1 基类名,...,继承方式n 基类名{
派生类新增的数据成员和成员函数
};
冒号后面的部分称为基类表,各基类之间用逗号分隔,其中“继承方式i”(i=1,2,...,n),规定了派生类从基类中按什么方式继承:private、protected、public。默认的继承方式是private。
例如:
class z:public x,y{ //类z公有继承了类x,私有继承了y(默认的)
...
};
class z:x,public y{ //类z私有继承了类x(默认的),公有继承了y
...
};
class z:public x,public y{ //类z公有继承了类x和类y
...
};
在多种继承中,三种继承方式对于基类成员在派生类中的访问属性规则与单继承相同。
//例4.13 多重继承情况下派生类的访问特性。
#include<iostream> using namespace std; class X{ //声明基类X public: void setX(int x) { a = x; } void showX() { cout<<"a="<<a<<endl; } private: int a; }; class Y{ //声明基类Y public: void setY(int y) { b = y; } void showY() { cout<<"b="<<b<<endl; } private: int b; }; class Z:public X,private Y{ //声明派生类Z,公有继承了类X,私有继承了类Y public: void setZ(int x,int y) { c = y; setY(x); } void showZ() { showY(); cout<<"c="<<c<<endl; } private: int c; }; int main() { Z obj; obj.setX(3); //正确,成员函数setX在Z中仍是公有成员 obj.showX(); //正确,成员函数showX在Z中仍是公有成员 //obj.setY(3); //错误,成员函数setY在Z中成为私有成员 //obj.showY(); //错误,成员函数setY在Z中成为私有成员 obj.setZ(6,8); obj.showZ(); return 0; }
说明:对基类成员的访问必须是无二义的,例如下列程序段对基类成员的访问是二义的,必须想办法消除二义性。
class X{ public: int f(); }; class Y{ public: int f(); int g(); }; class Z:public X,public Y{ public: int g(); int h(); };
如定义类Z的对象obj:
Z obj;
则以下对函数f()的访问是二义性的:
obj.f(); //二义性错误,不知调用的是类X的f(),还是类Y的f()
使用成员名限定可以消除二义性,例如:
obj.X::f(); //调用了类X的f()
obj.Y::f(); //调用了类Y的f()
4.4.2 多重继承派生类构造函数和析构函数
多重继承下派生类构造函数的定义形式与单继承派生类构造函数的定义形式相似,只是n个基类的构造函数之间用逗号分隔。多重继承下派生类构造函数定义的一般形式如下:
派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),...,基类名n(参数表n)
{
派生类新增成员的初始化语句
}
多重继承的构造函数的执行顺序与单继承构造函数的执行顺序相同,也是遵循先执行基类的构造函数,在执行对象成员的构造函数,最后执行派生类的构造函数体的原则。处于同一层次的各个基类构造函数的执行顺序,取决于声明派生所指定的各个基类的顺序,与派生类构造函数中所定义的成员初始化列表的各项顺序没有关系。由于析构函数不带参数,在派生类中是否要定义析构函数与它所属的基类无关,所以与单继承情况类似,基类的析构函数不会因为派生类没有析构函数而得不到执行,它们是各自独立的。析构函数的执行顺序与构造函数的执行顺序相反。
例如,由一个硬件类Hard和一个软件类Soft共同派生出计算机系统类System,声明如下:
class Hard{ //声明硬件类Hard protected: char bodyname[20]; public: Hard(char* bn); //基类Hard的构造函数 ... }; class Soft{ //声明软件类soft protected: char os[10]; char Lang[15]; public: Soft(char* o,char* lg); //基类Soft的构造函数 ... }; class System:public Hard,public Soft{ //声明计算机系统类System(派生类) private: char owner[10]; public: System(char* ow,char* bn,char* o,char* lg):Hard(bn),Soft(o,lg); ... //派生类System的构造函数,缀上了基类Hard和Soft的构造函数 }; 注意:这是一段示意性的程序段,表示在定义计算机系统派生类System的构造函数时,缀上了硬件基类Hard 和软件基类Soft的构造函数。 再如,现有一个窗口类Window和一个滚动条类Scrollbar,它们可以共同派生出一个带有滚动条的窗口,声明如下: class Window{ //声明窗口类 ... public: Window(int top,int left,int bottom,int right); ~Window(); ... }; class Scrollbar{ //声明滚动条类 ... public: Scrollbar(int top,int left,int bottom,int right); ~Scrollbar(); ... }; class ScrollbarWind:Window,Scrollbar{ //声明带有滚动条的窗口派生类 ... Scrollbar(int top,int left,int bottom,int right); ~Scrollbar(); ... }; Scrollbar::Scrollbar(int top,int left,int bottom,int right) :Window(top,left,bottom,right),Scrollbar(top,right-20,bottom,right) { ... } 这也是一段示意性的程序大段,表示定义带有滚动条窗口派生类ScrollbarWind的构造 函数时,也缀上了窗口基类Window和滚动条基类Scrollbar的构造函数。
例4.14 多重继承情况下派生类构造函数和析构函数的定义方法。
#include<iostream> using namespace std; class X{ public: X(int sa) //基类X的构造函数 { a = sa; cout<<"X_Construction called."<<endl; } int getX() { return a; } ~X() //基类X的析构函数 { cout<<"X_Destruction called."<<endl; } private: int a; }; class Y{ public: Y(int sb) //基类Y的构造函数 { b = sb; cout<<"Y_Construction called."<<endl; } int getY() { return b; } ~Y() //基类Y的析构函数 { cout<<"Y_Destruction called."<<endl; } private: int b; }; class Z:public X,private Y{ public: Z(int sa,int sb,int sc):X(sa),Y(sb) { c = sc; cout<<"Z_Construction called."<<endl; } int getZ() { return c; } Y::getY; //访问声明 //int getY() //覆盖基类Y的getY成员函数 //{ // return Y::getY(); //} ~Z() { cout<<"Z_Destruction called."<<endl; } private: int c; }; int main() { Z obj(2,4,6); int ma=obj.getX(); cout<<"a="<<ma<<endl; int mb=obj.getY(); cout<<"b="<<mb<<endl; int mc=obj.getZ(); cout<<"c="<<mc<<endl; return 0; } /* 运行结果是: X_Construction called. Y_Construction called. Z_Construction called. a=2 b=4 c=6 Z_Destruction called. Y_Destruction called. X_Destruction called. */