第5章 继承和派生

96
第5第 第第第第第 5.1 第第第第第第 5.2 第第第 5.3 第第第 5.4 第第第

description

第5章 继承和派生. 5.1 基类和派生类 5.2 单继承 5.3 多继承 5.4 虚基类. 5.1 基类和派生类. 5.1.1 派生类的定义格式 5.1.2 派生类的三种继承方式 5.1.3 访问控制 5.1.4 基类和派生类的关系. 返回首页. 称已存在的用来派生新类的类为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。派生类可以具有基类的特性,共享基类的成员函数,使用基类的数据成员,还可以定义自己的新特性,定义自己的数据成员和成员函数。. 图5-1 类之间的继承与派生关系. 5.1.1 派生类的定义格式. - PowerPoint PPT Presentation

Transcript of 第5章 继承和派生

Page 1: 第5章  继承和派生

第 5 章 继承和派生

5.1 基类和派生类 5.2 单继承 5.3 多继承 5.4 虚基类

Page 2: 第5章  继承和派生

5.1 基类和派生类

5.1.1 派生类的定义格式 5.1.2 派生类的三种继承方式 5.1.3 访问控制 5.1.4 基类和派生类的关系

返回首页

Page 3: 第5章  继承和派生

称已存在的用来派生新类的类为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。派生类可以具有基类的特性,共享基类的成员函数,使用基类的数据成员,还可以定义自己的新特性,定义自己的数据成员和成员函数。

Page 4: 第5章  继承和派生

图 5-1 类之间的继承与派生关系

Page 5: 第5章  继承和派生

5.1.1 派生类的定义格式

单继承的定义格式如下:class< 派生类名 > : < 继承方式 >< 基类名 >{public : // 派生类新定义成员

members;<private : >

members;<protected : >

members;};

Page 6: 第5章  继承和派生

多继承的定义格式如下:class< 派生类名 > : < 继承方式 1>< 基类名 1>,< 继

承方式 2>< 基类名 2>,…{public: // 派生类新定义成员members;

<private: >members;

<protected:>members;

};

Page 7: 第5章  继承和派生

例 5-1 :写出下列程序的执行结果。#include <iostream.h>

class demo

{protected:

int j;

public:

demo(){j=0};

void add(int i){j+=i;}

void display()

{ cout<<"Current value of j is"<<j<<endl;}

};

class child:public demo //类 child 继承了 demo 类{ char ch;

public:

void sub(int i){j-=i;}

};

Page 8: 第5章  继承和派生

void main(void){ child object,obj; // 两个对象的 j 均为 0 object.display(); object.add(10); object.display(); object.sub(5); object.display(); //object的 j=5 obj.display(); //obj的 j=0}此程序的运行结果为:current value of j is 0current value of j is 10current value of j is 5current value of j is 0

返回本节

Page 9: 第5章  继承和派生

5.1.2 派生类的三种继承方式

在 介 绍 公 有 继 承 ( public ) 、 私 有 继 承( private )和保护继承( protected )的继承方式前,先看一个例子。

例 5-2 :汽车类 vehicle 和其派生类小车类 car的定义。

class vehicle { int wheels ; // 车轮数float weight ; // 汽车重量

public :

Page 10: 第5章  继承和派生

void initialize (int in_wheels , float in_weight ); // 初始化数据成员

int get_wheels ( ) ; // 获取车轮数 float get_weight ( ) ; // 获取车重};class car : vehicle // 默认声明,私

有继承 { int passenger_load : // 载客量public :

void initialize (int in_wheels,float in_weight,int people=4);int passengers ( ) ; // 返回载客数

};

Page 11: 第5章  继承和派生

常用的三种继承方式:

1.公有继承( public )公有继承的特点是基类的公有成员和保护成员作为派生类

的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的。

2.私有继承( private )私有继承的特点是基类的公有成员和保护成员作为派生类

的私有成员,并且不能被这个派生类的子类访问。 3.保护继承( protected )保护继承的特点是基类的所有公有成员和保护成员都成为

派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。

Page 12: 第5章  继承和派生

表 5-1 不同继承方式的基类和派生类特性

Page 13: 第5章  继承和派生

例 5-3 :调用基类的公有成员示例程序。 #include<iostream.h>#include<math.h>class point{public:

void initp(float xx=0,float yy=0){x=xx;y=yy;}void move(float xoff,float yoff){x+=xoff;y+=yoff;}float getx(){return x;}float gety(){return y;}

private:float x,y;

};class rectangle:public point{

Page 14: 第5章  继承和派生

public:void initr(float x,float y,float w,float h){ initp(x,y); // 调用基类的公有成员

W=w;H=h;}

float getH(){return H;}float getW(){return W;}

private:float W,H;

};void main(){

rectangle rect;rect.initr(2,3,20,10);rect.move(3,2);cout<<"the data of rect(x,y,w,h):"<<endl;

cout<<rect.getx()<<","<<rect.gety()<<","<<rect.getW()<<","<<

rect.getH()<<endl;}

Page 15: 第5章  继承和派生

例 5-4 :私有继承示例程序。

#include<iostream.h>#include<math.h>class point{ public:

void initp(float xx=0,float yy=0){x=xx;y=yy;}void move(float xoff,float yoff){x+=xoff;y+=yoff;}float getx(){return x;}float gety(){return y;}

private:float x,y;

};class rectangle:private point{

Page 16: 第5章  继承和派生

public:void initr(float x,float y,float w,float h){initp(x,y);W=w;H=h;}void move(float xoff,float yoff){point::move(xoff,yoff);}float getx(){return point::getx();}float gety(){return point::gety();}float getH(){return H;}float getW(){return W;}

private:float W,H;

};void main(){

Page 17: 第5章  继承和派生

rectangle rect;

rect.initr(2,3,20,10);

rect.move(3,2);

cout<<"the data of rect(x,y,w,h):"<<endl;

cout<<rect.getx()<<","<<rect.gety()<<","<<rect.getW()<<","<<

rect.getH()<<endl;

}

返回本节

Page 18: 第5章  继承和派生

5.1.3 访问控制

类通过派生定义,形成类的等级,派生类中用“类名 :: 成员”访问基类成员。在建立一个类等级后,通常创建某个派生类的对象来使用这个类等级,包括隐含使用基类的数据和函数。

派生类对基类成员可以有不同的访问方式: 派生类可以覆盖基类成员 派生类不能访问基类的私有成员 基类的公有段和保护段成员访问权对派生类保

持不变(公有继承) 基类的公有段和保护段成员成为派生类的私有

成员(私有继承)

Page 19: 第5章  继承和派生

例 5-6 :给出以下程序的运行结果。

#include < iostream.h >class Base{ public : Base ( ) { cout << "\nBase created.\n"

; }} ;class D_class : public Base{ public : D_class ( ) { cout << "D_class

created.\n" ; }} ;main ( ){ D_class d1 ; } 此程序的运行结果为:Base created.D_class created.

Page 20: 第5章  继承和派生

1.定义与派生类同名的成员

例如:class base{ public : int a , b ; };class derived : public base{ public : int b , c ; } ;void f ( ){ derived d ; d . a = 1 ; d . base :: b = 2 ; // base :: b 使 用 的 是

base 类的数据成员 b d . b = 3 ; // 这里使用的是 derived 类的数据成

员 b d . c = 4 ;};

Page 21: 第5章  继承和派生

2 .派生类不能访问基类私有成员

例如:class X{public : void get_ij ( ) ; void put_ij ( ) ; private : int i , j ;};class Y : public X{public : int get_k ( ) ;void make_k ( ) ; private : int k ;};void Y :: make_k ( ) ;{ k = i * j ; // 非法} ;

Page 22: 第5章  继承和派生

3.公有继承

例 5-7 :派生类对基类的公有继承示例。class X{ protected : int i , j ; public :

void get_ij( ) { cout<<“Enter two numbers:”; cin>>i>>j;};

void put_ij( ) { cout<<i<<" "<<j<<'\n';};};class Y : public X{ int k ; public :

int get_k( ) { return k ; };

void make_k( )

Page 23: 第5章  继承和派生

{ k = i * j ; }; // 使用基类成员 i, j};class Z : public Y{ public : void f( ) { i = 2 ; j = 3; }; // 使用基类成员 i, j};main ( ){ Y var1;Z var2 ; var1.get_ij( ) ; var1.put_ij( ) ; var1.make_k( ) ; var2.f( ) ; var2.put_ij( ) ;}

Page 24: 第5章  继承和派生

4.私有继承和保护继承

私有继承:派生类对基类的私有继承使用关键字 private 描述(可缺省),基类的所有公有段和保护段成员都成为派生类的私有成员。

保护继承:派生类对基类的保护继承使用关键字 protected 描述,基类的所有公有段和保护段成员都成为派生类的保护成员,保护继承在程序中很少应用。

Page 25: 第5章  继承和派生

例 5-8 :派生类对基类的私有继承示例。 class X{protected : int i , j ; public :

void get_ij ( ) {cout<<“Enter two numbers:”; cin>>i>>j;};void put_ij ( ) {cout<<i<<“ ”<<j<<‘\n’;};

};class Y : private X{ int k ; public :

int get_k ( ) { return k ; };void make_k ( ) { k = i * j ; } ;

} ;class Z : public Y{ public : void f( )

Page 26: 第5章  继承和派生

{ i = 2 ; j = 3 ; // error

};

};

main ( )

{ Y var1; Z var2 ;

var1 . get_ij ( ) ; // error

var1 . put_ij ( ) ; // error

var1 . make_k ( ) ;

cout << var1 . get_k ( ) << ‘\n’ ;

var2 . put_ij ( ) ; // error

}

Page 27: 第5章  继承和派生

图 5-2 类的层次关系示意图

Page 28: 第5章  继承和派生

5.派生类中的静态成员

例如:class B{ public :

static void f ( ) ;void g ( ) ;

} ; class D : private B { } ;class DD : public D { void h ( ) ; } ;void DD :: h ( ){ B :: f ( ) ; // ok f ( ) ; // error g ( ) ; // error} ;

Page 29: 第5章  继承和派生

6.访问声明

例如:class B{ int a ;public :

int d , c ; int bf ( ) ;

} ;class D : private B{ int d ;public :

B :: c ;// 调整对 B :: c 的访问控制int e ;int df ( ) ;

} ;返回本节

Page 30: 第5章  继承和派生

5.1.4 基类和派生类的关系

1.派生类是基类的具体化类的层次通常反映了客观世界中某种真实的模型。 2.派生类是基类定义的延续先定义一个抽象基类,该基类中有些操作并未实

现。 3.派生类是基类的组合在多继承时,一个派生类有多于一个的基类,这

时派生类将是所有基类行为的组合。

返回本节

Page 31: 第5章  继承和派生

5.2 单继承

5.2.1 成员访问权控制 5.2.2 派生与构造函数、析构函数 5.2.3 继承中构造函数的调用顺序 5.2.4 组合 5.2.5 子类型和类型适应

返回首页

Page 32: 第5章  继承和派生

5.2.1 成员访问权控制

例 5-9 : public 继承方式访问权限的例子。#include <iostream.h>// 定义基类 Aclass A{public:    A() { cout<<"类 A 的构造函数! "<<endl; }    A(int a) { Aa = a, aa = a, aaa = a; }    void Aprint() { cout<<" 类 A 打 印 自 己 的private 成员 aa:"<<aa<<endl; }

    int Aa;private:

Page 33: 第5章  继承和派生

  int aa;protected:    int aaa;};// 定义由基类 A 派生的类 Bclass B : public A{public:    B() { cout<<"类 B 的构造函数! "<<endl; }    B(int i, int j, int k);    void Bprint() { cout<<" 类 B 打印自己的 private 成员 bb 和 protected 成员

bbb:" <<bb<<","<<bbb<<endl; }    void B_Aprint()

Page 34: 第5章  继承和派生

{ cout<<"类 B的 public 函数访问类 A的 public 数据成员Aa:"<<Aa<<endl;

    cout<<"类 B的 public 函数访问类 A的 protected 数据成员 aaa:"<<aaa<<endl;

    GetAaaa();    GetAaaa1();}private:    int bb;    void GetAaaa() { cout<<"类 B的 private 函数访问类

A的 public 数据成员Aa:"<<Aa<<endl; 

    cout<<"类 B的 private 函数访问类 A的 protected 数据成员 aaa:"<<aaa<<endl;}

protected:    int bbb;

Page 35: 第5章  继承和派生

void GetAaaa1() { cout<<"类 B的 protected 函数访问类 A的 public 数据成员Aa:"<<Aa<<endl;

    cout<<"类 B的 protected 函数访问类 A的 protected 数据成员 aaa:"<<aaa<<endl;}

};

//类 B 的构造函数,需负责对基类 A 的构造函数的初始化B::B(int i, int j, int k):A(i), bb(j), bbb(k) {}

// 程序主函数void main()

{

Page 36: 第5章  继承和派生

B b1(100, 200, 300); // 定义类 B 的一个对象 b1 ,并初始化构造函数和基类构造函数

    b1.B_Aprint(); // 类 B 调用自己的成员函数 B_Aprint 函数

    b1.Bprint(); // 类 B 对象 b1访问自己的 private和 protected 成员

    b1.Aprint(); // 通过类 B 的对象 b1 调用类 A的 public 成员函数

}

Page 37: 第5章  继承和派生

该程序的输出结果为:类 B的 public 函数访问类 A的 public 数据成员 Aa:100

类 B的 public 函数访问类 A的 protected 数据成员 aaa:100

类 B的 private 函数访问类 A的 public 数据成员 Aa:100

类 B的 private 函数访问类 A的 protected 数据成员 aaa:100

类 B的 protected 函数访问类 A的 public 数据成员 Aa:100

类 B的 protected 函数访问类 A的 protected 数据成员 aaa:100

类 B 打 印 自 己 的 private 成 员 bb 和 protected 成 员bbb:200,300

类 A 打印自己的 private 成员 aa:100

返回本节

Page 38: 第5章  继承和派生

5.2.2 派生与构造函数、析构函数

1.构造函数派生类的对象的数据结构由基类中说明的数据成

员和派生类中说明的数据成员共同构成。将派生类的对象中由基类中说明的数据成员和操作所构成的封装体称为基类子对象,它由基类中的构造函数进行初始化。构造函数不能够被继承。

Page 39: 第5章  继承和派生

派生类构造函数的一般格式如下 < 派生类名 > ( < 派生类构造函数总参数表 > ): < 基类

构造函数 > ( < 参数表 1> ) ,< 子对象名 > ( < 参数表 2> ){< 派生类中数据成员初始化 >

};派生类构造函数的调用顺序如下:( 1 )调用基类的构造函数,调用顺序按照它们继承时说

明的顺序。( 2 )调用子对象类的构造函数,调用顺序按照它们在类

中说明的顺序。( 3 )派生类构造函数体中的内容。

Page 40: 第5章  继承和派生

例 5-10 :构造派生类构造函数的例子。 #include <iostream.h>class A{public: A() { a=0; cout<<"类 A 的缺省构造函数 .\n"; } A(int i) { a=i; cout<<" 类 A 的 构 造 函 数 .\

n"; } ~A() { cout<<"类 A 的析构函数 .\n"; } void Print() const { cout<<a<<","; } int Geta() { return a; }private: int a;};

Page 41: 第5章  继承和派生

class B : public A{public: B() { b=0; cout<<"类 B 的缺省构造函数 .\n"; } B(int i, int j, int k); ~B() { cout<<"类 B 的析构函数 .\n"; } void Print();private: int b; A aa;};B::B(int i, int j, int k):A(i), aa(j){ b=k; cout<<"类 B 的构造函数 .\n"; }void B::Print(){

Page 42: 第5章  继承和派生

A::Print(); cout<<b<<","<<aa.Geta()<<endl;}void main(){ B bb[2]; bb[0] = B(1, 2, 3); bb[1] = B(4, 5, 6); for(int i=0; i<2; i++) bb[i].Print();}

Page 43: 第5章  继承和派生

运行结果为:类 A 的缺省构造函数 .类 A 的缺省构造函数 .类 B 的缺省构造函数 .类 A 的缺省构造函数 .类 A 的缺省构造函数 .类 B 的缺省构造函数 .类 A 的构造函数 .类 A 的构造函数 .类 B 的构造函数 .类 B 的析构函数 .类 A 的析构函数 .类 A 的析构函数 .

Page 44: 第5章  继承和派生

类 A 的构造函数 .类 A 的构造函数 .类 B 的构造函数 .类 B 的析构函数 .类 A 的析构函数 .类 A 的析构函数 .1,3,24,6,5类 B 的析构函数 .类 A 的析构函数 .类 A 的析构函数 .类 B 的析构函数 .类 A 的析构函数 .类 A 的析构函数 .

Page 45: 第5章  继承和派生

2.析构函数 例 5-11 :析构函数执行顺序示例程序。 #include<iostream.h>class B1{public:

B1(int i){cout<<"constructing B1"<<i<<endl;}~B1(){cout<<"destructing B1"<<endl;};

};class B2{public:

B2(int j){cout<<"constructing B2"<<j<<endl;}~B2(){cout<<"destructing B2"<<endl;}

};class B3{

Page 46: 第5章  继承和派生

public:B3(){cout<<"constructing B3"<<endl;}~B3(){cout<<"destructing B3"<<endl;}

};class C:public B2,public B1,public B3{public:

C(int a,int b,int c,int d):B1(a),memberB2(d),memberB1(c),B2(b){}

private:B1 memberB1;B2 memberB2;B3 memberB3;

};void main(){

C obj(1,2,3,4);}

Page 47: 第5章  继承和派生

运行结果为:constructing B22constructing B11constructing B3constructing B13constructing B24constructing B3destructing B3destructing B2destructing B1destructing B3destructing B1destructing B2

Page 48: 第5章  继承和派生

3.派生类构造函数使用中应注意的问题

( 1 )派生类构造函数的定义中可以省略对基类构造函数的调用,其条件是在基类中必须有默认的构造函数或者根本没有定义构造函数。

( 2 )当基类的构造函数使用一个或多个参数时,则派生类必须定义构造函数,提供将参数传递给基类构造函数的途径。

Page 49: 第5章  继承和派生

例 5-12 :请写出下列程序的运行结果。 #include<iostream.h>class Vehicle // 汽车类{protected: int numWheels; // 车轮数 int range; // 汽车活动范围public: Vehicle(int w,int r):numWheels(w),range(r){} void ShowV() { cout<<”Wheels:”<<numWheels<<endl; cout<<”Range:”<<range<<endl; }};

Page 50: 第5章  继承和派生

class Car : public Vehicle// 小轿车{ int passengers; //乘客数public: Car(int w,int r, int

p):Vehicle(w,r),passengers(p){} void Show() { cout<<"\nCar:\n"; ShowV(); cout<<"Passengers: "<<passengers<<endl; }} void main(){ Car c(4,500,5); c.Show();}

Page 51: 第5章  继承和派生

例 5-13 :给出以下程序的运行结果。#include <iostream.h>

class parent_class

{ int private1 , private2 ;

public :

parent_class ( int p1 , int p2 )

{ private1 = p1; private2 = p2; }// 基类有一个参数化的构造函数

Page 52: 第5章  继承和派生

int inc1 ( ) { return + + private1; }

int inc2 ( ) { return + + private2 ; }

void display ( )

{cout << "private1=" << private1 << " , private2=" << private2 << endl ; }

};

class derived_class : private parent_class

{ int private3 ;

parent_class private4 ;

public:

Page 53: 第5章  继承和派生

derived_class ( int p1 , int p2 , int p3 , int p4 , int p5 )

// 派生类的构造函数有 5 个参数: parent_class ( p1 , p2 ) , private4 ( p3 , p4 )

// 参数 p1 , p2 用于调用构造函数 parent_class ( p1 , p2 )

// 初始化基类的私有数据 private1 和 private2

// 参数 p3 , p4 用于调用构造函数 private4 的 parent_class ( p1 , p2 )

// 初始化对象成员私有数据 private4 . private1 和 private4 . private2

// 参数 p5 初始化派生类私有数据 private3

Page 54: 第5章  继承和派生

{ private3 = p5 ; } int inc1 ( ) { return parent_class :: inc1

( ) ; } int inc3 ( ) { return + + private3 ; } void display ( ) { parent_class :: display ( ) ; private4 . parent_class :: display ( ) ; cout << "private3=" << private3 << endl ; }} ;main ( ){ derived_class d1 ( 17 , 18 , 1 , 2 , -5 ) ; d1 . inc1 ( ) ; // 基类私有数据 private1 加 1 

d1 . display ( ) ;}

Page 55: 第5章  继承和派生

此程序的运行结果为:private1 = 18 , private2 = 18

private1 = 1 , private2 = 2

private3 = -5

返回本节

Page 56: 第5章  继承和派生

5.2.3 继承中构造函数的调用顺序 例 5-14 :给出以下程序的运行结果。#include<iostream.h>class base1{protected:

char c; public:

base1(char ch);~base1();

};base1::base1(char ch){

c=ch;cout<<"c="<<c<<endl;cout<<"base1 构造函数被调用 !"<<endl;

}

Page 57: 第5章  继承和派生

base1::~base1(){

cout<<"base1 析构函数被调用! "<<endl;}class base2{protected:

int i; public:

base2(int j);~base2();

};base2::base2(int j){

i=j;cout<<"i="<<i<<endl;cout<<"base2 构造函数被调用 !"<<endl;

}

Page 58: 第5章  继承和派生

base2::~base2(){

cout<<"base2 析构函数被调用 !"<<endl;}class derive:public base1,public base2{ int k;public:

derive(char ch,int i,int kk);//:base1(ch),base2(i)~derive();

};derive::derive(char ch,int ii,int

kk):base1(ch),base2(ii),k(kk){ //k=kk; cout<<"k="<<k<<endl; cout<<"derive 构造函数被调用 !"<<endl<<endl;

Page 59: 第5章  继承和派生

}derive::~derive(){

cout<<"derive 析构函数被调用 !"<<endl;}void main(){

derive A('B',10,15);}此程序的运行结果为:c=Bbase1 构造函数被调用!i=10base2 构造函数被调用!k=15derive 构造函数被调用!derive 析构函数被调用!base2 析构函数被调用!base1 析构函数被调用!

返回本节

Page 60: 第5章  继承和派生

5.2.4 组合 例 5-15 :组合示例程序。class Vehicle{ //... };class Motor{ //...};class Car:public Vehicle{ public:

Motor motor; // 包含一个公共对象数据成员

Page 61: 第5章  继承和派生

};

void vehicleFn(Vehicle& v);

void metorFn(Motor& m);

void main( )

{

car c;

vechileFn(c);

motorFn(c); //错误motorFn(c.motor);

}

返回本节

Page 62: 第5章  继承和派生

5.2.5 子类型和类型适应

1.子类型化子类型的概念涉及到行为共享,它与继承有着密切关系。

有一个特定的类型 S ,当且仅当它至少提供了类型 T 的行为,又称类型 S 是类型 T 的子类型。子类型是类型之间的一般和特殊的关系。

2.类型适应类型适应是指两种类型之间的关系。例如, B 类

型适应 A 类型是指 B 类型的对象能够用于 A 类型的对象所能使用的场合。

返回本节

Page 63: 第5章  继承和派生

5.3 多继承

5.3.1 多继承的概念 5.3.2 多继承的构造函数 5.3.3 二义性和支配原则 5.3.4 赋值兼容规则

返回首页

Page 64: 第5章  继承和派生

5.3.1 多继承的概念

可以为一个派生类指定多个基类,这样的继承结构称为多继承。多继承可以看作是单继承的扩展。所谓多继承是指派生类具有多个基类,派生类与每个基类之间的关系仍可看作是一个继承。

多继承下派生类的定义格式如下:class< 派生类名 > : < 继承方式 1>< 基类名 1>,< 继承

方式 2>< 基类名 2>,…{< 派生类类体 >};其中, < 继承方式 1> 、 < 继承方式 2> 、…是三种继承方

式: public , private 或 protected之一。

Page 65: 第5章  继承和派生

例 5-16 :多继承示例程序。 class A{ public : void setA ( int x ) { a = x ; } ; void showA( ) { cout << a << endl ; } ; private : int a ;} ;class B{ public : void setB ( int x ) { b = x ; } ; void showB ( ) { cout << b << endl ; } ; private : int b ;} ;class C : public A , private B // C 公有继承

A ,私有继承 B { public :

Page 66: 第5章  继承和派生

void setC ( int x , int y , int z ) {setA(x); setB(y); c=z;};

void showC ( ) { showA ( ) ; showB ( ) ; cout << c << endl ; } ;

private : int c ;} ; main ( ){ C obj ; obj . setA ( 5 ) ; obj . showA ( ) ; obj . setC ( 6 , 7 , 9 ) ; obj . showC ( ) ; obj . setB ( 6 ) ; // error obj . showB ( ) ; // error}

返回本节

Page 67: 第5章  继承和派生

5.3.2 多继承的构造函数

派生类的构造函数格式如下:< 派生类名 > ( < 总参数表 > ): < 基类名 1>

( < 参数表 1> ) ,< 基类名 2> ( < 参数表 2> ) ,…< 子对象名 > ( < 参数表 n+1>) ,…

{

< 派生类构造函数体 >

}

其中, < 总参数表 > 中各个参数包含了其后的各个分参数表。

Page 68: 第5章  继承和派生

例 5-18 :一个多重继承的示例。类 Software 由 两 个 类 OperatingSystem 和

Application 派 生 , 而 类 Computer 又 由 类Software 和另一个类 Hardware 派生,如图 5-3 所示。

Page 69: 第5章  继承和派生

图 5-3 多重继承示意图

Page 70: 第5章  继承和派生

源程序如下:#include <string.h>#include <iostream.h>#include <iomanip.h>class OperatingSystem{    private:        char OsName[80];    public:        const char *GetOsName() const        {            return OsName;        }        OperatingSystem()        {        }

Page 71: 第5章  继承和派生

OperatingSystem(char *OsName)        {            strcpy(OperatingSystem::OsName,OsName);        }};class Application{    private:        char *(AppName[80]);        char n;    public:        const char *GetAppName(int i) const        {            return AppName[i];

Page 72: 第5章  继承和派生

}        int AddAppName(char *AppName)        {            char *NewAppName=new char[80];            strcpy(NewAppName,AppName);            Application::AppName[++n]=NewAppName;            return n;        }        int GetAppCount()        {            return n;        }        Application()        {            n=0;        }        ~Application()        {

Page 73: 第5章  继承和派生

  for (int i=1;i<=n;i++)                delete AppName[i];        }};class Software : public OperatingSystem, public

Application{    public:        Software(char *OsName)        : OperatingSystem(OsName),Application()        {        }};class Hardware{    private:        char CpuType[80];    public:        void SetCpuType(char* CpuType)        {

Page 74: 第5章  继承和派生

  strcpy(Hardware::CpuType,CpuType);        }        char *GetCpuType()        {            return CpuType;        }        Hardware()       {        }};class ComputerSystem : public Hardware, public Software

{

Page 75: 第5章  继承和派生

public:

        ComputerSystem(char *CpuType,char *OsName)

        : Hardware(),Software(OsName)

        {

            SetCpuType(CpuType);

        }

};

void main()

{

    ComputerSystem MyComputer("AMD K6-200","Windows NT 4.0");

    cout<<"My computer's CPU: "<<MyComputer.GetCpuType()<<endl;

    cout<<"Operating System: "<<MyComputer.GetOsName()<<endl;

    MyComputer.AddAppName("Microsoft Visual C++");

    MyComputer.AddAppName("Microsoft Word 97");

    cout<<MyComputer.GetAppCount()<<" applications:"<<endl;

    for (int i=1;i<=MyComputer.GetAppCount();i++)

    cout<<"\t"<<MyComputer.GetAppName(i)<<endl;

}

返回本节

Page 76: 第5章  继承和派生

5.3.3 二义性和支配原则

1.同名成员的二义性 2.同一基类被多次继承产生的二义性

Page 77: 第5章  继承和派生

1.同名成员的二义性

例 5-19 :给出以下程序的运行结果。 class A {   public:         int a()         {             return 1;     } }; class B :public A {    public:       float a()         {             return float(1.2345);     }

Page 78: 第5章  继承和派生

}; class C : public A { }; class D : public B, public C { }; void main() {    D d;     cout<<d.a()<<endl; }

Page 79: 第5章  继承和派生

2.同一基类被多次继承产生的二义性

下面是一个多重继承的例子:class base{    public:        int b;}class base1:public base { };class base2:public base { };class derived:public base1,public base2{    public:        int b;};

返回本节

Page 80: 第5章  继承和派生

5.3.4 赋值兼容规则

1.派生类对象可以赋值给基类对象derived d;base b;b=d;2.派生类对象可以初始化基类的引用derived d;base& br=d;3.派生类对象的地址可以赋给指向基类的指针derived d;base *pb=&d;

返回本节

Page 81: 第5章  继承和派生

5.4 虚基类

5.4.1 虚基类的引入 5.4.2 虚基类的构造函数

返回首页

Page 82: 第5章  继承和派生

5.4.1 虚基类的引入

如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性。

例如:class B { public : int b ;} ;class B1 : public B { private : int b1 ; } ;class B2 : public B{ private : int b2 ; } ;class C : public B1 , public B2 {

Page 83: 第5章  继承和派生

public : int f ( ) ;

private : int d ;

} ;

有:C c ;

c . b // error

c . B :: b // error ,从哪里继承的?c . B1 :: b // ok ,从 B1 继承的c . B2 :: b // ok ,从 B2 继承的

Page 84: 第5章  继承和派生

图 5-4 类的层次关系示意图

Page 85: 第5章  继承和派生

例 5-20 :给出以下程序的运行结果。

class B { public : int b ;} ;class B1 : public B { private : int b1 ; } ;class B2 : public B { private : int b2 ; } ;class C : public B1 , public B2 { public : int f ( ) ; private : int d ; } ; main ( ){ C c ; c . B1 :: b = 5 ; c . B2 :: b = 10 ; cout << "path B1==>" <<c . B1 :: b << endl ; cout << "path B2==>" << c . B2 :: b << endl ;}此程序的运行结果为:path B1==> 5path B2==> 10

Page 86: 第5章  继承和派生

此程序中,多重派生类 C 的对象的存储结构如图 5-5 所示。

Page 87: 第5章  继承和派生

图 5-6 类 A、类 B、类 C和类 D关系示意图

Page 88: 第5章  继承和派生

图 5-7 各类存储结构示意图

Page 89: 第5章  继承和派生

图 5-8 类间层次示意图

Page 90: 第5章  继承和派生

图 5-9 带有虚基类的派生类 C的对象的存储结构示意图

Page 91: 第5章  继承和派生

例 5-21 :给出以下程序的运行结果。

#include < iostream.h >class A{ public : A ( ) { cout << "class A" << endl ; } } ;class B : virtual public A{ public : B ( ) {cout << "class B" << endl ; } } ;class C : virtual public A{ public : C ( ) {cout << "class C" << endl ; } } ;class D : public B , public C{ public : D ( ) {cout << "class D" << endl ; } } ;main ( ) { D dd ; }

Page 92: 第5章  继承和派生

此程序的运行结果为:class A

class B

class C

class D

返回本节

Page 93: 第5章  继承和派生

5.4.2 虚基类的构造函数

为了初始化基类的子对象,派生类的构造函数要调用基类的构造函数。对于虚基类来讲,由于派生类的对象中只有一个虚基类子对象。

C++规定,在一个成员初始化列表中出现对虚基类和非虚基类构造函数的调用,则虚基类的构造函数先于非虚基类的构造函数执行。

从虚基类直接或间接继承的派生类中的构造函数的成员初始化列表中都要列出这个虚基类构造函数的调用。

Page 94: 第5章  继承和派生

例 5-23 :具有虚基类的派生类的构造函数用法示例程序。 #include <iostream.h>class A{public:    A(const char *s) { cout<<s<<endl; }    ~A() {}};class B : virtual public A{public:    B(const char *s1, const char *s2):A(s1)    {        cout<<s2<<endl;    }};

Page 95: 第5章  继承和派生

class C : virtual public A{public:    C(const char *s1, const char *s2):A(s1)    {    cout<<s2<<endl;    }};class D : public B, public C{public:    D(const char *s1, const char *s2, const char *s3,

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

Page 96: 第5章  继承和派生

}};void main(){    D *ptr = new D("class A", "class B", "class C", "class D");

    delete ptr;}该程序的输出结果为:class Aclass Bclass Cclass D

返回本节