第14讲多继承和虚基类 教学目的与要求: 了解虚基类的声明和作用。 掌握多基派生类的声明和构造函数,虚基类 教学内容提要: 1、多继承的声明; 2、多继承的构造函数和析构函数; 3、虚基类; °教学重点:多继承的派生类的声明和构造函数。 教学难点:多继承的派生类的声明和构造函数。 教学进度:P136P148 教学过程
•教学目的与要求: 了解虚基类的声明和作用。 掌握多基派生类的声明和构造函数,虚基类 •教学内容提要: 1、 多继承的声明; 2、多继承的构造函数和析构函数; 3、虚基类; •教学重点:多继承的派生类的声明和构造函数。 •教学难点:多继承的派生类的声明和构造函数。 •教学进度:P136~P148 •教学过程: 第 14 讲 多继承和虚基类
(14,1多继承的声明】 多继承可以看作是单继承的扩充,它是指由多个基类派生出 个新类的情形 声明形式如下: class派生类名:派生类型1基类名1,派生类型2基类名2, 派生类型n基类名n 派生类新成员声明 每一个派生类型对应的是紧接其后给出的基类,而且必须给每 个基类指定一种派生类型,如果缺省,相应的基类则取私有派 生类型,而不是和前一个基类取相同的派生类型
多继承可以看作是单继承的扩充,它是指由多个基类派生出一 个新类的情形。 声明形式如下: class 派生类名:派生类型1 基类名1,派生类型2 基类名2,… 派生类型n 基类名n { 派生类新成员声明 }; 每一个派生类型对应的是紧接其后给出的基类,而且必须给每 个基类指定一种派生类型,如果缺省,相应的基类则取私有派 生类型,而不是和前一个基类取相同的派生类型。 【 14.1 多继承的声明】
比如: class a lass B class C: public A, B 说明:类A派生类C时,采用的是公有派 生,也即类A中的公有和保护成员被类C 继承过来且成员类型保持不变,但类C中 例141(多继承) 的成员函数不能访问类A的私有成员 #includesiostream.h> class a public: void setx(int a)x=a; 1 void sety(int b)y=b; 1
多继承 #include class A { public: void setx(int a){x=a;} void sety(int b){y=b;} 比如: class A { ... }; class B { ... }; class C:public A,B { ... } 说明:类A派生类C时,采用的是公有派 生,也即类A中的公有和保护成员被类C 继承过来且成员类型保持不变,但类C中 的成员函数不能访问类A的私有成员。 例 14-1
int getxo constreturnx (续) int geto constreturn y; protected: int x private int y, class B publIc: void setzlint czc int getz const return protected: it z; class c: public a, private b public void setcz(int cset(c); int getczo constreturnz; 3 int getsumo const(return x+gety o+z
int getx() const{return x;} int gety() const{return y;} protected: int x; private: int y; }; class B { public: void setz(int c){z=c;} int getz() const {return z;} protected: int z; }; class C:public A,private B { public: void setCz(int c){setz(c);} int getCz() const{return z;} int getsum() const{return x+gety()+z;} (续)
续 void maino csetx (2); csety( 3); c.setcz5) cout<<"X"<<c getx(<<tY=N<<c.getyo<<tzF<<c. getczo<<endl; cout<<X+Y+lF<<cgetsum(<<endl; 2 Y=3 X+Y+Z=10 多继承可以看作是多个单继承的组合,单继承可以看作是多继承的特例
X=2 Y=3 Z=5 X+Y+Z=10 多继承可以看作是多个单继承的组合,单继承可以看作是多继承的特例。 }; void main() { C c; c.setx(2); c.sety(3); c.setCz(5); cout<<″X=″<<c.getx()<<″\tY=″<<c.gety()<<″\tZ=″<<c.getCz()<<endl; cout<<″X+Y+Z=″<<c.getsum()<<endl; } (续)
(14.2多继承的构造函数和析构函数】 多继承派生类的构造函数的一般形式如下 参数总表应包括初始化基类数据成员、内嵌对 象数据成员及其他数据成员所需的全部数据。 派生类名::派生类名(参数总表):基类1(参数表1),基类2(参 数表2),…,基类n(参数表n),对象成员1(对象成员参数表1),对象成 员2(对象成员参数表2),…,对象成员n(对象成员参数表n) 派生类中新声明的数据成员初始化语句 是需要用参数初始化的基类名、对象成员名及各自对应的参数表,基类名 和对象成员名之间的顺序可以是任意的,且对于使用默认构造函数的基类 和对象成员,可以不列出基类名和对象成员名。对象成员是指在派生类中 新声明的数据成员,它是属于另外一个类的对象。对象成员必须在初始化 列表中初始化
派生类名::派生类名(参数总表):基类1(参数表1),基类2(参 数表2),…,基类n(参数表n),对象成员1(对象成员参数表1),对象成 员2(对象成员参数表2),…,对象成员n(对象成员参数表n) { 派生类中新声明的数据成员初始化语句 } 参数总表应包括初始化基类数据成员、内嵌对 象数据成员及其他数据成员所需的全部数据。 是需要用参数初始化的基类名、对象成员名及各自对应的参数表,基类名 和对象成员名之间的顺序可以是任意的,且对于使用默认构造函数的基类 和对象成员,可以不列出基类名和对象成员名。对象成员是指在派生类中 新声明的数据成员,它是属于另外一个类的对象。对象成员必须在初始化 列表中初始化。 多继承派生类的构造函数的一般形式如下: 【 14.2 多继承的构造函数和析构函数】
派生类构造函数执行的一般顺序如下 (1)调用基类的构造函数,调用顺序与声明派生类时基类在声明语句中 出现的顺序一致。 (2)调用对象成员的构造函数,调用顺序是按照它们在派生类声明中的 顺序。 (3)调用派生类的构造函数。 例14-2(多继承时构造函数的调用 #includesiostream.h> class a public: AO{cout<"调用类A的构造函数AO"<<end;} A(int a, int b) cout<调用类A的构造函数Aint,int)"<endl;
#include class A { public: A(){cout<<″调用类A的构造函数A()″<<endl;} A(int a,int b) { x=a; y=b; cout<<″调用类A的构造函数A(int,int)″<<endl; } 例 14-2 多继承时构造函数的调用 (1) 调用基类的构造函数,调用顺序与声明派生类时基类在声明语句中 出现的顺序一致。 (2) 调用对象成员的构造函数,调用顺序是按照它们在 派生类声明中的 顺序。 (3) 调用派生类的构造函数。 派生类构造函数执行的一般顺序如下:
AO{cout<"调用类A的析构函数<<endl;} (续) protected: int x: private: int class B public BO{cout<"调用类B的构造函数B0"<<end B(int c) cout<"调用类B的构造函数B(int)"<end; BO{cout<<"调用类B的析构函数<<endl;} protected int z, class c
~A(){cout<<″调用类 A的析构函数″<<endl;} protected: int x; private: int y; }; class B { public: B(){cout<<″调用类 B的构造函数B()″<<endl;} B(int c) { z=c; cout<<″调用类 B的构造函数B(int)″<<endl; }~B(){cout<<″调用类 B的析构函数″<<endl;} protected: int z; }; class C (续)
public 续) CO{cout<"调用类C的构造函数"<endl;} CO{cout<<"调用类C的析构函数<<end;} class d: public B, private A, public C public: DO{cout<"调用类D的构造函数DO"<endl;} D(int a, int b, int c, int d, int e, int f, int g: A(a, b), a(d, e), b(, B(c), w(g cout<"调用类D的构造函数D(int,int,int,int, int int, in)"<<endl; ~DO{cout<调用类D的析构函数<<end;} private 说明:在类D的构造函数的初始化列表中没 A 有列出基类C和内嵌对象成员c,因此系统 Bb 在创建类D的对象时,将两次调用类C的默 int w: 认构造函数,分别对从类C继承的成员和内 嵌对象c进行初始化
{ public: C(){cout<<″调用类C的构造函数″<<endl;} ~C(){cout<<″调用类C的析构函数″<<endl;} }; class D:public B,private A,public C { public: D(){cout<<″调用类D的构造函数D()″<<endl;} D(int a,int b,int c,int d,int e,int f,int g):A(a,b),a(d,e),b(f),B(c),w(g) { cout<<″调用类D的构造函数D(int,int,int,int,int,int,int)″<<endl; } ~D(){cout<<″调用类D的析构函数″<<endl;} private: C c; A a; B b; int w; }; 说明:在类D的构造函数的初始化列表中没 有列出基类C和内嵌对象成员c,因此系统 在创建类D的对象时,将两次调用类C的默 认构造函数,分别对从类C继承的成员和内 嵌对象c进行初始化。 (续)
void main( (续) d dI Dd2(1,2,3,10,20,30,40); 调用类B的构造函数B0 调用类A的构造函数AO 调用类C的构造函数 调用类C的构造函数 调用类A的构造函数AO 调用类B的构造函数BO 调用类D的构造函数DO 调用类B的构造函数Bint) 调用类A的构造函数(int,int) 调用类C的构造函数 调用类C的构造函数 调用类A的构造函数Aint,int) 调用类B的构造函数Bint) 调用类D的构造函数D(int, int int, intint,intint)
void main() { D d1; D d2(1,2,3,10,20,30,40); } 调用类B的构造函数B() 调用类A的构造函数A() 调用类C的构造函数 调用类C的构造函数 调用类A的构造函数A() 调用类B的构造函数B() 调用类D的构造函数D() 调用类B的构造函数B(int) 调用类A的构造函数A(int,int) 调用类C的构造函数 调用类C的构造函数 调用类A的构造函数A(int,int) 调用类B的构造函数B(int) 调用类D的构造函数D(int,int,int,int,int,int,int) (续)