88
9 9 9.1 第第第第第第 9.1 第第第第第第 9.2 9.2 9.3 9.3 9.4 9.4

第 9 章 继 承 性

  • Upload
    ashby

  • View
    195

  • Download
    6

Embed Size (px)

DESCRIPTION

第 9 章 继 承 性. 9.1 基类和派生类 9.2 单 继 承 9.3 多 继 承 9.4 虚 基 类. 继承性是面向对象程序设计的一种重要功能,是实现代码复用的一种形式。继承可以使程序设计人员在一个已存在类的基础上很快建立一个新的类,而不必从零开始设计新类。新设计类能够具有原有类的属性和方法,并且为了使新类具有自己独特的功能,新类还要添加新的属性和方法。 当一个类被其他的类继承时,被继承的类称为基类,又称为父类、超类。继承其他类属性和方法的类称为派生类,又称为子类、继承类。. 9.1 基类和派生类. 9.1.1 派生类的定义 - PowerPoint PPT Presentation

Citation preview

Page 1: 第 9 章  继  承  性

第第 99 章 继 承 性章 继 承 性 9.1 基类和派生类9.1 基类和派生类

9.2 单 继 承9.2 单 继 承

9.3 多 继 承9.3 多 继 承

9.4 虚 基 类9.4 虚 基 类

Page 2: 第 9 章  继  承  性

继承性是面向对象程序设计的一种重要功能,是实现代码复用的一种形继承性是面向对象程序设计的一种重要功能,是实现代码复用的一种形式。继承可以使程序设计人员在一个已存在类的基础上很快建立一个新式。继承可以使程序设计人员在一个已存在类的基础上很快建立一个新的类,而不必从零开始设计新类。新设计类能够具有原有类的属性和方的类,而不必从零开始设计新类。新设计类能够具有原有类的属性和方法,并且为了使新类具有自己独特的功能,新类还要添加新的属性和方法,并且为了使新类具有自己独特的功能,新类还要添加新的属性和方法。法。当一个类被其他的类继承时,被继承的类称为基类,又称为父类、超类。当一个类被其他的类继承时,被继承的类称为基类,又称为父类、超类。继承其他类属性和方法的类称为派生类,又称为子类、继承类。继承其他类属性和方法的类称为派生类,又称为子类、继承类。

Page 3: 第 9 章  继  承  性

9.1 9.1 基类和派生类基类和派生类9.1.1 9.1.1 派生类的定义派生类的定义

派生能用从派生类到基类的箭头图形表示,派生能用从派生类到基类的箭头图形表示,箭头指向基类表示派生类引用基类中的函箭头指向基类表示派生类引用基类中的函数和数据,而基类则不能访问派生类,如数和数据,而基类则不能访问派生类,如图图 9-19-1 所示。任何一个类均可作为基类所示。任何一个类均可作为基类。。。。从从一个基类派生的继承称为单继承。一个基类派生的继承称为单继承。

Page 4: 第 9 章  继  承  性

图9-1

子类

基类

Page 5: 第 9 章  继  承  性

单继承声明语句的一般形式为:单继承声明语句的一般形式为:

class <class < 派生类名派生类名 > : <> : < 继承方式继承方式 > <> < 基类名基类名 >>

{{

数据成员和成员函数声明数据成员和成员函数声明

}}

Page 6: 第 9 章  继  承  性

基类可分为两类:直接基类和间接基类。如果某个基类在基类列表基类可分为两类:直接基类和间接基类。如果某个基类在基类列表中提及,则称它是直接基类。例如:中提及,则称它是直接基类。例如:class Aclass A

{ };{ };

class B:public A //class B:public A // 类类 AA 为直接基类。为直接基类。{ };{ };

Page 7: 第 9 章  继  承  性

间接基类可写为:间接基类可写为:class Aclass A

{ };{ };

class B:public Aclass B:public A

{ };{ };

class C:public B //class C:public B // 类类 AA 是间接基类,可扩展到任意级数是间接基类,可扩展到任意级数{ };{ };

Page 8: 第 9 章  继  承  性

9.1.2 9.1.2 继承方式继承方式

继承方式有继承方式有 33 种:公有继承方式(种:公有继承方式( publicpublic )、私有继)、私有继承方式(承方式( privateprivate )和保护继承方式()和保护继承方式( protectedprotected )。)。

Page 9: 第 9 章  继  承  性

9.1.2.1 9.1.2.1 公有继承公有继承  

在公有派生类中:在公有派生类中:(( 11 )基类的公有成员在派生类中仍是公有成员。)基类的公有成员在派生类中仍是公有成员。(( 22 )基类的保护成员在派生类中仍是保护成员。)基类的保护成员在派生类中仍是保护成员。(( 33 )基类的私有成员在派生类中是不可访问的。)基类的私有成员在派生类中是不可访问的。

Page 10: 第 9 章  继  承  性

9.1.2.2 9.1.2.2 私有继承私有继承  

在私有派生类中:在私有派生类中:(( 11 )基类的公有成员在派生类中是私有成员。)基类的公有成员在派生类中是私有成员。(( 22 )基类的保护成员在派生类中是私有成员。)基类的保护成员在派生类中是私有成员。(( 33 )基类的私有成员在派生类中仍是不可访问的。)基类的私有成员在派生类中仍是不可访问的。

Page 11: 第 9 章  继  承  性

9.1.2.3 9.1.2.3 保护继承保护继承  

在保护派生类中:在保护派生类中:(( 11 )基类的公有成员在派生类中是保护成员。)基类的公有成员在派生类中是保护成员。(( 22 )基类的保护成员在派生类中是保护成员。)基类的保护成员在派生类中是保护成员。(( 33 )基类的私有成员在派生类中仍是不可访问的。)基类的私有成员在派生类中仍是不可访问的。

Page 12: 第 9 章  继  承  性

9.2 9.2 单 继 承单 继 承9.2.1 9.2.1 单继承中的成员访问权限单继承中的成员访问权限

  

(( 11 )公有成员:一个类的公有成员允许)公有成员:一个类的公有成员允许本类的成员函数、本类的对象、公有派生本类的成员函数、本类的对象、公有派生类的成员函数、公有派生类的对象访问。类的成员函数、公有派生类的对象访问。

Page 13: 第 9 章  继  承  性

(( 22 )私有成员:一个类的私有成员只允许本类的成员函数访)私有成员:一个类的私有成员只允许本类的成员函数访问。问。

(( 33 )保护成员:具有私有成员和公有成员的特征。一个类的)保护成员:具有私有成员和公有成员的特征。一个类的保护成员允许本类的成员函数、公有派生类的成员函数访问。保护成员允许本类的成员函数、公有派生类的成员函数访问。本类的对象、公有派生类的对象不能访问。本类的对象、公有派生类的对象不能访问。

Page 14: 第 9 章  继  承  性

【例【例 9.19.1 】】成员访问权限举例。成员访问权限举例。class A //class A // 基类基类{{private:private:int privA;int privA;protected:protected:int protA;int protA;public:public:int pubA;int pubA;};};

Page 15: 第 9 章  继  承  性

class B : public Aclass B : public A // // 派生类派生类{{public:public:void fn()void fn(){{int a;int a;a = privA;a = privA; // // 错误:不可访问错误:不可访问a = protA;a = protA; // // 有效有效a = pubA;a = pubA; // // 有效有效}}};};

Page 16: 第 9 章  继  承  性

void main()void main(){{A a; //A a; // 基类对象基类对象a.privA = 1; //a.privA = 1; // 错误:不可访问错误:不可访问a.protA = 1; //a.protA = 1; // 错误:不可访问错误:不可访问a.pubA = 1; //a.pubA = 1; // 有效有效B b; //B b; // 派生类对象派生类对象b.privA = 1; //b.privA = 1; // 错误:不可访问错误:不可访问b.protA = 1; //b.protA = 1; // 错误:不可访问错误:不可访问b.pubA = 1; //b.pubA = 1; // 有效有效}}

Page 17: 第 9 章  继  承  性

【例【例 9.29.2 】】分析下面的程序。分析下面的程序。#include "iostream.h"#include "iostream.h"class Aclass A{{private:private:int x;int x;public:public:void f1(int a);void f1(int a);int f2();int f2();};};

Page 18: 第 9 章  继  承  性

class B:public Aclass B:public A

{{

private:private:

int y;int y;

public:public:

void g1(int a);void g1(int a);

int g2();int g2();

};};

Page 19: 第 9 章  继  承  性

void A::f1(int a)void A::f1(int a)

{{

x=a;x=a;

}}

int A::f2()int A::f2()

{{

return x;return x;

}}

Page 20: 第 9 章  继  承  性

void B::g1(int a)void B::g1(int a)

{{

y=a;y=a;

}}

int B::g2()int B::g2()

{{

return y+f2();return y+f2();

}}

Page 21: 第 9 章  继  承  性

void main()void main()

{{

B b;B b;

b.f1(10);b.f1(10);

b.g1(10);b.g1(10);

cout<<b.g2()<<endl;cout<<b.g2()<<endl;

}}

运行程序,输出结果为:运行程序,输出结果为: 2020

Page 22: 第 9 章  继  承  性

9.2.2 9.2.2 构造函数和析构函数构造函数和析构函数9.2.2.1 9.2.2.1 构造函数构造函数

派生类的数据是由基类中的数据和在派生类中新定义的数据组成。派生类的数据是由基类中的数据和在派生类中新定义的数据组成。由于构造函数不能够继承。因此,在定义派生类的构造函数时,除由于构造函数不能够继承。因此,在定义派生类的构造函数时,除了对自己新定义的数据成员进行初始化外,还必须调用基类的构造了对自己新定义的数据成员进行初始化外,还必须调用基类的构造函数使基类的数据成员得以初始化。函数使基类的数据成员得以初始化。

Page 23: 第 9 章  继  承  性

【例【例 9.39.3 】】分析下面的程序。分析下面的程序。class Baseclass Base{{protected: protected: int a;int a;public:public:Base(){ a = 0;} //Base(){ a = 0;} // 默认构造函数默认构造函数Base(int c) { a = c;} Base(int c) { a = c;} //// 单参数构造函数单参数构造函数};};

Page 24: 第 9 章  继  承  性

class Derived: public Baseclass Derived: public Base

{{

public:public:

Derived():Base(){}; //Derived():Base(){}; // 默认构造函数默认构造函数Derived(int c):Base(c){}; //Derived(int c):Base(c){}; // 单参数构造函数单参数构造函数};};

Page 25: 第 9 章  继  承  性

【例【例 9.49.4 】】分析下面的程序。分析下面的程序。class engineclass engine

{{

private:private:

int num;int num;

public: public:

engine(int s) { num = s; }engine(int s) { num = s; }

};};

Page 26: 第 9 章  继  承  性

class jetclass jet{{private:private:int jt;int jt;engine eobj;engine eobj; //// 这里声明一个对象这里声明一个对象public:public:jet(int x, int y): eobj(y)jet(int x, int y): eobj(y){{jt = x;jt = x;}}};};

Page 27: 第 9 章  继  承  性

总结派生类的构造函数的调用顺序如下:总结派生类的构造函数的调用顺序如下:

(( 11 )基类的构造函数;)基类的构造函数;

(( 22 )子对象类的构造函数(如果子对象存在);)子对象类的构造函数(如果子对象存在);

(( 33 )派生类的构造函数。)派生类的构造函数。

Page 28: 第 9 章  继  承  性

9.2.2.2 9.2.2.2 析构函数析构函数析构函数的调用顺序与构造函数相反,析构函数首先为派生类调用,析构函数的调用顺序与构造函数相反,析构函数首先为派生类调用,然后为子对象类的析构函数调用,最后调用基类的析构函数。仅当然后为子对象类的析构函数调用,最后调用基类的析构函数。仅当派生类的构造函数通过动态内存管理分配内存时,才定义派生类的派生类的构造函数通过动态内存管理分配内存时,才定义派生类的析构函数。如果派生类的构造函数不起任何作用或派生类中未添加析构函数。如果派生类的构造函数不起任何作用或派生类中未添加任何附加数据成员,则派生类的析构函数可以是一个空函数。任何附加数据成员,则派生类的析构函数可以是一个空函数。

Page 29: 第 9 章  继  承  性

【例【例 9.59.5 】】分析下面的程序。分析下面的程序。##include "iostream.h"include "iostream.h"

class Aclass A

{{

private:private:

int x;int x;

public: public:

A(){x=0;}A(){x=0;}

Page 30: 第 9 章  继  承  性

A(int xx){x=xx;}A(int xx){x=xx;}

~A(){cout<<"A Destructor called."<<endl;}~A(){cout<<"A Destructor called."<<endl;}

void display(){cout<<x<<" ";}void display(){cout<<x<<" ";}

};};

Page 31: 第 9 章  继  承  性

class B:public Aclass B:public A

{{

private:private:

int b1;int b1;

A b2; //A b2; // 这里声明一个对象这里声明一个对象public:public:

B(){b1=0;}B(){b1=0;}

B(int i,int j):b2(j){b1=i;}B(int i,int j):b2(j){b1=i;}

Page 32: 第 9 章  继  承  性

B(int i, int j,int k): A(i),b2(j),b1(k){}B(int i, int j,int k): A(i),b2(j),b1(k){}~B(){cout<<"B Destructor called."<<endl;}~B(){cout<<"B Destructor called."<<endl;}void print()void print(){{display();display();cout<<b1<<" ";cout<<b1<<" ";b2.display();b2.display();cout<<endl;cout<<endl;}}};};

Page 33: 第 9 章  继  承  性

void main()void main()

{{

B obj1;B obj1;

B obj2(5,6);B obj2(5,6);

B obj3(7,8,9);B obj3(7,8,9);

obj1.print();obj1.print();

obj2.print();obj2.print();

obj3.print();obj3.print();

}}

Page 34: 第 9 章  继  承  性

程序运行结果为:程序运行结果为:0 0 00 0 00 5 60 5 67 9 87 9 8B Destructor called.B Destructor called.A Destructor called.A Destructor called.A Destructor called.A Destructor called.B Destructor called.B Destructor called.A Destructor called.A Destructor called.A Destructor called.A Destructor called.B Destructor called.B Destructor called.A Destructor called.A Destructor called.A Destructor called.A Destructor called.

Page 35: 第 9 章  继  承  性

9.2.2.3 9.2.2.3 调用成员函数调用成员函数派生类中的成员函数与基类中的成员函数可以有相同的名称。当使用基派生类中的成员函数与基类中的成员函数可以有相同的名称。当使用基类的对象调用函数时,基类的函数被调用。当使用派生类对象的名称时,类的对象调用函数时,基类的函数被调用。当使用派生类对象的名称时,派生类的函数被调用。如果派生类的成员函数要调用相同名称的基类函派生类的函数被调用。如果派生类的成员函数要调用相同名称的基类函数,它必须使用作用域运算符数,它必须使用作用域运算符 :::: 。基类中的函数既可使用基类的对象,。基类中的函数既可使用基类的对象,也可使用派生类的对象调用。如果函数存在于派生类而不是基类中,那也可使用派生类的对象调用。如果函数存在于派生类而不是基类中,那么它只能被派生类的对象调用。 么它只能被派生类的对象调用。

Page 36: 第 9 章  继  承  性

【例【例 9.69.6 】】分析下面的程序。分析下面的程序。class Baseclass Base

{{

protected:protected:

int ss;int ss;

public:public:

int func()int func() {return ss;}{return ss;}

void print(){cout<<ss;}void print(){cout<<ss;}

};};

Page 37: 第 9 章  继  承  性

class Derived: public Baseclass Derived: public Base

{{

public:public:

int func()int func() { return Base::func(); }{ return Base::func(); }

};};

Page 38: 第 9 章  继  承  性

void main()void main()

{{

Base b1; //Base b1; // 基类对象基类对象b1.func();b1.func(); // // 调用基类函数调用基类函数 funcfunc

Derived a1; //Derived a1; // 派生类对象派生类对象a1.func();a1.func(); // // 调用派生类对象调用派生类对象 funcfunc

}}

Page 39: 第 9 章  继  承  性

9.3 9.3 多 继 承多 继 承9.3.1 9.3.1 多继承的概念多继承的概念

从多个基类派生的继承称为多继承,或称从多个基类派生的继承称为多继承,或称多重继承,即一个派生类可以有多个直接多重继承,即一个派生类可以有多个直接基类。基类。

Page 40: 第 9 章  继  承  性

多继承声明语句的一般形式为:多继承声明语句的一般形式为:class <class < 派生类名派生类名 >> :: < < 继承方式继承方式 > <> < 基类名基类名 1>1> ,, < < 继承方式继承方式 > <> < 基基类名类名 2>2> ,, ······

{{

数据成员和成员函数声明数据成员和成员函数声明}} ;;

Page 41: 第 9 章  继  承  性

例如:例如:class Aclass A{{······};};class Bclass B{{······};};class C:public A,public Bclass C:public A,public B{{······};};

Page 42: 第 9 章  继  承  性

【例【例 9.79.7 】】分析下面的程序。分析下面的程序。#include "iostream.h"#include "iostream.h"

class Aclass A

{{

public:public:

void printA(){cout<<"Hello ";}void printA(){cout<<"Hello ";}

};};

Page 43: 第 9 章  继  承  性

class Bclass B

{{

public:public:

void printB(){cout<<"C++ ";}void printB(){cout<<"C++ ";}

};};

class C: public A,public Bclass C: public A,public B

{{

public:public:

void printC(){cout<<"World!\n";}void printC(){cout<<"World!\n";}

};};

Page 44: 第 9 章  继  承  性

void main()void main()

{{

C obj;;C obj;;

obj.printA();obj.printA();

obj.printB();obj.printB();

obj.printC();obj.printC();

}}

Page 45: 第 9 章  继  承  性

程序执行结果为:程序执行结果为:

Hello C++ World!Hello C++ World!

Page 46: 第 9 章  继  承  性

9.3.2 9.3.2 多继承的构造函数和析构函数多继承的构造函数和析构函数 多基派生类的构造函数的一般形式为:多基派生类的构造函数的一般形式为:<< 派生类名派生类名 >> :::: << 派生类名派生类名 >> (〈参数表(〈参数表 11 〉,〈参数表〉,〈参数表 22 〉,〉, ······):): << 基类名基类名1>1> (〈参数表(〈参数表 11 〉),〉), << 基类名基类名 2>2> (〈参数表(〈参数表 22 〉),〉), ······

{{

<< 派生类成员派生类成员 >>

}}

Page 47: 第 9 章  继  承  性

多重继承的构造函数按照下面的原则被调用多重继承的构造函数按照下面的原则被调用 ::

(( 11 )先基类,后自己。)先基类,后自己。(( 22 )如果在同一层上有多个基类,按照派生时定义的先后顺序)如果在同一层上有多个基类,按照派生时定义的先后顺序执行。执行。多重继承的析构函数的执行顺序与多重继承的构造函数的执行顺多重继承的析构函数的执行顺序与多重继承的构造函数的执行顺序相反。序相反。

Page 48: 第 9 章  继  承  性

【例【例 9.89.8 】】分析下列程序的输出结果。分析下列程序的输出结果。#include "iostream.h"#include "iostream.h"

class Base1class Base1

{{

private:private:

int a1;int a1;

public:public:

Page 49: 第 9 章  继  承  性

Base1(int i){a1=i;cout<<"Constructor Base1 called Base1(int i){a1=i;cout<<"Constructor Base1 called "<<a1<<endl;}"<<a1<<endl;}

~Base1(){cout<<"Desstructor Base1 called"<<endl;}~Base1(){cout<<"Desstructor Base1 called"<<endl;}

};};

Page 50: 第 9 章  继  承  性

class Base2class Base2

{{

private:private:

int a2;int a2;

public:public:

Base2(int j){a2=j;cout<<"Constructor Base2 called "<<a2<<endl;}Base2(int j){a2=j;cout<<"Constructor Base2 called "<<a2<<endl;}

~Base2(){cout<<"Desstructor Base2 called"<<endl;}~Base2(){cout<<"Desstructor Base2 called"<<endl;}

};};

Page 51: 第 9 章  继  承  性

class Base3class Base3

{{

private:private:

int a3;int a3;

public:public:

Base3(int k=0){a3=k;cout<<"Constructor Base3 called "<<a3<<endl;}Base3(int k=0){a3=k;cout<<"Constructor Base3 called "<<a3<<endl;}

~Base3(){cout<<"Desstructor Base3 called"<<endl;}~Base3(){cout<<"Desstructor Base3 called"<<endl;}

};};

Page 52: 第 9 章  继  承  性

class Derived:public Base3,public Base1,public Base2class Derived:public Base3,public Base1,public Base2

{{

private:private:

int a4;int a4;

Base1 obj1;Base1 obj1;

Base2 obj2;Base2 obj2;

Base3 obj3;Base3 obj3;

Page 53: 第 9 章  继  承  性

public:public:Derived(int i,int j,int k,int m,int n):obj2(m),obj3(j),obj1(k),Base2(i),Base1(j)Derived(int i,int j,int k,int m,int n):obj2(m),obj3(j),obj1(k),Base2(i),Base1(j){{a4=n;a4=n;cout<<"Constructor Derived called "<<a4<<endl;cout<<"Constructor Derived called "<<a4<<endl;}}~Derived(){cout<<"Desstructor Derived called"<<endl;}~Derived(){cout<<"Desstructor Derived called"<<endl;}};};

Page 54: 第 9 章  继  承  性

void main()void main()

{{

Derived obj(1,2,3,4,5);Derived obj(1,2,3,4,5);

}}

Page 55: 第 9 章  继  承  性

程序输出结果:程序输出结果:Constructor Base3 called 0Constructor Base3 called 0

Constructor Base1 called 2Constructor Base1 called 2

Constructor Base2 called 1Constructor Base2 called 1

Constructor Base1 called 3Constructor Base1 called 3

Constructor Base2 called 4Constructor Base2 called 4

Constructor Base3 called 2Constructor Base3 called 2

Constructor Derived called 5Constructor Derived called 5

Page 56: 第 9 章  继  承  性

Desstructor Derived calledDesstructor Derived called

Desstructor Base3 calledDesstructor Base3 called

Desstructor Base2 calledDesstructor Base2 called

Desstructor Base1 calledDesstructor Base1 called

Desstructor Base2 calledDesstructor Base2 called

Desstructor Base1 calledDesstructor Base1 called

Desstructor Base3 calledDesstructor Base3 called

Page 57: 第 9 章  继  承  性

注意:注意:执行基类构造函数的顺序取决于定义派生执行基类构造函数的顺序取决于定义派生

类时基类的顺序。在派生类构造函数的成员初始类时基类的顺序。在派生类构造函数的成员初始

化列表中各项顺序可以任意地排列。化列表中各项顺序可以任意地排列。

Page 58: 第 9 章  继  承  性

9.3.3 9.3.3 二义性问题二义性问题  

在多继承情况下,当两个基类有相同的函数或数据成员名在多继承情况下,当两个基类有相同的函数或数据成员名称时,编译器将不能理解使用哪个函数,出现对基类成员称时,编译器将不能理解使用哪个函数,出现对基类成员访问不唯一的情况,称为对基类成员访问的二义性问题。访问不唯一的情况,称为对基类成员访问的二义性问题。

Page 59: 第 9 章  继  承  性

例如:例如:class Alphaclass Alpha

{{

public:public:

void display();void display();

};};

Page 60: 第 9 章  继  承  性

class Betaclass Beta

{{

public:public:

void display();void display();

};};

class Gamma: public Alpha,public Betaclass Gamma: public Alpha,public Beta

{{

};};

Page 61: 第 9 章  继  承  性

void main()void main()

{{

Gamma obj;Gamma obj;

obj.display(); obj.display();

//// 含义模糊:不能编译含义模糊:不能编译}}

Page 62: 第 9 章  继  承  性

若要访问正确的函数或数据成员,需要使用作用域运算若要访问正确的函数或数据成员,需要使用作用域运算符符 :::: 。。例如:例如:obj.Alpha::display();obj.Alpha::display();

obj.Beta::display();obj.Beta::display();

Page 63: 第 9 章  继  承  性

9.4 9.4 虚 基 类虚 基 类9.4.1 9.4.1 虚基类的引入和说明虚基类的引入和说明

  

引入虚基类就是为了解决二义性问题。引入虚基类就是为了解决二义性问题。虚基类说明格式如下:虚基类说明格式如下:virtual <virtual < 继承方式继承方式 ><>< 基类名基类名 >>

Page 64: 第 9 章  继  承  性

例如:例如:class windowclass window

{{

protected:protected:

int basedata;int basedata;

};};

Page 65: 第 9 章  继  承  性

class border: virtual public windowclass border: virtual public window

{ };{ };

class menu: virtual public windowclass menu: virtual public window

{ };{ };

class border_and_menu: public border, public menuclass border_and_menu: public border, public menu

{{

public:public:

Page 66: 第 9 章  继  承  性

int show()int show()

{{

return basedata;return basedata;

}}

};};

虚基类用在多重继承层次结构中,避免同一数据成员的不必要重复。虚基类用在多重继承层次结构中,避免同一数据成员的不必要重复。

Page 67: 第 9 章  继  承  性

9.4.2 9.4.2 虚基类的构造函数虚基类的构造函数虚基类的出现改变了构造函数的调用顺序。在初始化任何非虚基类的出现改变了构造函数的调用顺序。在初始化任何非虚基类之前,将先初始化虚基类。如果存在多个虚基类,初虚基类之前,将先初始化虚基类。如果存在多个虚基类,初始化顺序由它们在继承图中的位置决定,其顺序是从上到下、始化顺序由它们在继承图中的位置决定,其顺序是从上到下、从左到右。调用析构函数遵守相同的规则,但是顺序相反。 从左到右。调用析构函数遵守相同的规则,但是顺序相反。

Page 68: 第 9 章  继  承  性

【例【例 9.99.9 】】分析下列程序的输出结果。分析下列程序的输出结果。#include "iostream.h"#include "iostream.h"

class Aclass A

{{

public:public:

A(const char *s1){cout<<s1<<endl;}A(const char *s1){cout<<s1<<endl;}

~A(){cout<<"Destructor A called"<<endl;}~A(){cout<<"Destructor A called"<<endl;}

};};

Page 69: 第 9 章  继  承  性

class B:virtual public Aclass B:virtual public A

{{

public:public:

B(const char *s1,const char *s2):A(s1){cout<<s2<<endl;}B(const char *s1,const char *s2):A(s1){cout<<s2<<endl;}

~B(){cout<<"Destructor B called"<<endl;}~B(){cout<<"Destructor B called"<<endl;}

};};

Page 70: 第 9 章  继  承  性

class C:virtual public Aclass C:virtual public A

{{

public:public:

C(const char *s1,const char *s2):A(s1){cout<<s2<<endl;}C(const char *s1,const char *s2):A(s1){cout<<s2<<endl;}

~C(){cout<<"Destructor C called"<<endl;}~C(){cout<<"Destructor C called"<<endl;}

};};

Page 71: 第 9 章  继  承  性

class D:public B,public Cclass D:public B,public C{{public:public:D(const char *s1,const char *s2,const char *s3,const char *s4):B(s1,s2),C(s1,s3),A(s1)D(const char *s1,const char *s2,const char *s3,const char *s4):B(s1,s2),C(s1,s3),A(s1){{cout<<s4<<endl;cout<<s4<<endl;}}~D(){cout<<"Destructor D called"<<endl;}~D(){cout<<"Destructor D called"<<endl;}};};

Page 72: 第 9 章  继  承  性

void main()void main()

{{

D obj("class A","class B","class C","class D");D obj("class A","class B","class C","class D");

}}

Page 73: 第 9 章  继  承  性

程序运行结果为:程序运行结果为:class Aclass A

class Bclass B

class Cclass C

class Dclass D

Destructor D calledDestructor D called

Destructor C calledDestructor C called

Destructor B calledDestructor B called

Destructor A calledDestructor A called

Page 74: 第 9 章  继  承  性

9.4.3 9.4.3 虚基类的应用虚基类的应用【例【例 9.109.10 】】设计一个表示在职学生的类,分析下列程序的输出结设计一个表示在职学生的类,分析下列程序的输出结果。果。首先设计基类首先设计基类 peoplepeople ,表示一般人员的信息,在设计一个表示工,表示一般人员的信息,在设计一个表示工作人员的类作人员的类 jobjob ,接下来设计一个表示学生的类,接下来设计一个表示学生的类 studentstudent ,在职学,在职学生类以这些类为基类。生类以这些类为基类。

Page 75: 第 9 章  继  承  性

#include "iostream.h"#include "iostream.h"

#include "string.h"#include "string.h"

class peopleclass people

{{

private:private:

char name[20];char name[20];

char ID[20];char ID[20];

char sex;char sex;

int age;int age;

Page 76: 第 9 章  继  承  性

public:public:

people(char *n="",char *i="",char s='m',int a=19);people(char *n="",char *i="",char s='m',int a=19);

void pdisplay();void pdisplay();

};};

Page 77: 第 9 章  继  承  性

people::people(char *n,char *i,char s,int a)people::people(char *n,char *i,char s,int a)

{{

strcpy(name,n);strcpy(name,n);

strcpy(ID,i);strcpy(ID,i);

sex=s;sex=s;

age=a;age=a;

}}

Page 78: 第 9 章  继  承  性

void people::pdisplay()void people::pdisplay(){{cout<<"cout<<" 人员人员 :\n:\n身份证号 身份证号 ---"<<ID<<endl;---"<<ID<<endl;cout<<"cout<<"姓名 姓名 ---"<<name<<endl;---"<<name<<endl;if(sex=='m'||sex=='M')cout<<"if(sex=='m'||sex=='M')cout<<" 性别 性别 ---"<<"---"<<"男男"<<endl;"<<endl;else if(sex=='f'||sex=='F')cout<<"else if(sex=='f'||sex=='F')cout<<" 性别 性别 ---"<<"---"<<"女女"<<endl;"<<endl;cout<<"cout<<"年龄 年龄 ---"<<age<<endl;---"<<age<<endl;}}

Page 79: 第 9 章  继  承  性

class job:virtual public peopleclass job:virtual public people{{private:private:int number; //int number; //工作证号工作证号char department[20]; //char department[20]; //工作部门工作部门public:public:job(char *n,char *i,char s,int a,int num=0,char *dep="");job(char *n,char *i,char s,int a,int num=0,char *dep="");void jdisplay();void jdisplay();};};

Page 80: 第 9 章  继  承  性

job::job(char *n,char *i,char s,int a,int num,char job::job(char *n,char *i,char s,int a,int num,char *dep):people(n,i,s,a)*dep):people(n,i,s,a)

{{

number=num;number=num;

strcpy(department,dep);strcpy(department,dep);

}}

Page 81: 第 9 章  继  承  性

void job::jdisplay()void job::jdisplay()

{{

cout<<"cout<<"工作人员工作人员 :"<<endl;:"<<endl;

cout<<"cout<<" 编号 编号 ---"<<number<<endl;---"<<number<<endl;

cout<<"cout<<"工作单位 工作单位 ---"<<department<<endl;---"<<department<<endl;

}}

Page 82: 第 9 章  继  承  性

class student: virtual public peopleclass student: virtual public people

{{

private:private:

int snum;int snum;

int classnum;int classnum;

public:public:

student(char *n,char *i,char s,int a,int sn=0,int cn=0):people(n,i,s,a)student(char *n,char *i,char s,int a,int sn=0,int cn=0):people(n,i,s,a)

Page 83: 第 9 章  继  承  性

{{

snum=sn;snum=sn;

classnum=cn;classnum=cn;

}}

Page 84: 第 9 章  继  承  性

void sdisplay()void sdisplay()

{{

cout<<"cout<<" 在校学生在校学生 "<<endl;"<<endl;

cout<<"cout<<"学号学号="<<snum<<endl;="<<snum<<endl;

cout<<"cout<<"班级班级 ="<<classnum<<endl;="<<classnum<<endl;

}}

};};

Page 85: 第 9 章  继  承  性

class job_student:public job,public studentclass job_student:public job,public student

{{

public:public:

job_student(char *n,char *i,char s='m',int a=19,int mn=0,char *md="",int job_student(char *n,char *i,char s='m',int a=19,int mn=0,char *md="",int no=0,int sta=1):job(n,i,s,a,mn,md),student(n,i,s,a,no,sta),people(n,i,s,a)no=0,int sta=1):job(n,i,s,a,mn,md),student(n,i,s,a,no,sta),people(n,i,s,a)

{}{}

Page 86: 第 9 章  继  承  性

void tdisplay();void tdisplay();

};};

void job_student::tdisplay()void job_student::tdisplay()

{{

cout<<"cout<<" 在职学生在职学生 "<<endl;"<<endl;

}}

Page 87: 第 9 章  继  承  性

void main()void main()

{{

job_student w("job_student w("张国媛张国媛","122334571908655",'f',22,102,"","122334571908655",'f',22,102,"民族学院民族学院",5282,2004);",5282,2004);

w.tdisplay();w.tdisplay();

w.pdisplay();w.pdisplay();

w.jdisplay();w.jdisplay();

w.sdisplay();w.sdisplay();

}}

Page 88: 第 9 章  继  承  性

程序运行结果为:程序运行结果为:在职学生在职学生人员人员 ::身份证号 身份证号 ---122334571908655---122334571908655姓名 姓名 ------张国媛张国媛性别 性别 ------女女年龄 年龄 ---22---22工作人员工作人员 ::编号 编号 ---102---102工作单位 工作单位 ------民族学院民族学院在校学生在校学生学号学号=5282=5282班级班级 =2004=2004