71
第5第 第第第第第第

第 5 章 派生类与继承

  • Upload
    masato

  • View
    124

  • Download
    3

Embed Size (px)

DESCRIPTION

第 5 章 派生类与继承. 主要内容. 5.1 继承与派生类的概念 5.2 派生类成员的访问属性 5.3 派生类构造函数和析构函数 5.4 多继承 5.5 虚基类 5.6 子对象与父对象复制兼容 5.7 继承与组合. 5.1 继承与派生类. 5.1.1 为什么要使用继承 引入继承的目的在于为代码重用提供有效手段。. 5.1.2 派生类的声明. class employee{ protected: char name[10]; int age; - PowerPoint PPT Presentation

Citation preview

Page 1: 第 5 章  派生类与继承

第 5 章 派生类与继承

Page 2: 第 5 章  派生类与继承

主要内容5.1 继承与派生类的概念5.2 派生类成员的访问属性5.3 派生类构造函数和析构函数5.4 多继承5.5 虚基类5.6 子对象与父对象复制兼容5.7 继承与组合

Page 3: 第 5 章  派生类与继承

5.1 继承与派生类

5.1.1 为什么要使用继承 引入继承的目的在于为代码重用提供有效手段。

Page 4: 第 5 章  派生类与继承

class person {

protected:

char name[10];

int age;

char sex;

public:

void print();

};

class employee{

protected:

char name[10];

int age;

char sex;

char department[20];

float salary;

public:

void print();

};

5.1.2 派生类的声明

Page 5: 第 5 章  派生类与继承

// 定义一个基类 class person{ protected: char name[10]; int age; char sex; public: //…… }; // 定义一个派生类 class employee:public person { protected: char department[20]; float salary; public: //…… };

Page 6: 第 5 章  派生类与继承

声明一个派生类的一般格式为 :

class 派生类名 : 继承方式 基类名 {

// 派生类新增的数据成员和成员函数

};

Page 7: 第 5 章  派生类与继承

由类 person 继承出类 employee 可以采用下面的三种格式之一 : (1) 公有继承 class employee:public person{ //… }; (2) 私有继承 class employee:private person{ //… }; (3) 保护继承 class employee:protected person{ //… };

Page 8: 第 5 章  派生类与继承

从已有类派生出新类时 , 可以在派生类内完成以下几种功能 :

(1) 可以增加新的数据成员 ;

(2) 可以增加新的成员函数 ;

(3) 可以重新定义基类中已有的数据成员和成员函数 ;

(4) 可以改变现有成员的属性。

5.1.3 派生类生成过程

Page 9: 第 5 章  派生类与继承

5.2 派生类成员的访问属性

派生类对基类成员的访问形式主要有以下两种 :

(1) 内部访问 : 由派生类中新增成员对基类继承来的成员的访问。 (2) 对象访问 : 在派生类外部 , 通过派生类的对象对从基类继承来的成员的访问。

Page 10: 第 5 章  派生类与继承

5.2.1 公有继承的访问规则

基类的 public 成员公有继承派生类 public 成员;

基类的 protected 成员公有继承派生类 protected 成员;

基类的 private 成员公有继承派生类中不可访问。

例 5.1 一个公有继承的例子

Page 11: 第 5 章  派生类与继承

#include<iostream.h>

class A{ // 声明一个基类public:

void setA(int i,int j)

{ x=i; y=j; }

void showA()

{ cout<<”x=”<<x<<endl;

cout<<”y=”<<y<<endl; }

private:

int x;

protected:

int y;};

class B:public A // 声明一个公有派生类{

public:

void setB(int i,int j,int k)

{ setA(i,j); //setA() 在派生类中是 public 成员,可以访问

z=k;

}

void showB()

{

cout<<”x=”<<x<<endl;

cout<<”y=”<<y<<endl;

cout<<”z=”<<z<<endl;

}

private:

int z;};

int main()

{ B obj;

obj.setB(10,20,30);

obj.showA();

obj.showB();

return 0;

}

Page 12: 第 5 章  派生类与继承

表 5-1 公有继承的访问规则

基类成员 private 成员 public 成员 protected 成员

内部访问 对象访问 

不可访问不可访问

可访问 可访问

可访问不可访问

Page 13: 第 5 章  派生类与继承

5.2.2 私有继承的访问规则

基类的 public 成员私有继承派生类 private 成员;

基类的 protected 成员私有继承派生类 private 成员;

基类的 private 成员私有继承派生类中不可访问。

例 5.2 一个私有继承的例子

Page 14: 第 5 章  派生类与继承

#include<iostream.h>

class A{ // 声明一个基类public:

void setA(int i)

{ x=i; }

void showA()

{ cout<<x<<endl; }

private:

int x;

};

class B:private A{

public:

void setB(int i,int j)

{ setA(i);

y=j; }

void showB()

{ cout<<x;

cout<<y<<endl; }

private:

int y;

};

main()

{ B obj;

obj.setA(10);

obj.showA();

obj.setB(10,20);

obj.showB();

return 0;

}

Page 15: 第 5 章  派生类与继承

表 5-2 私有继承的访问规则

基类成员 private 成员 public 成员 protected 成员

内部访问对象访问 

不可访问不可访问

可访问不可访问

可访问不可访问

 

Page 16: 第 5 章  派生类与继承

5.2.3. 保护继承的访问规则

基类的 public 成员保护继承派生类 protected 成员;

基类的 protected 成员保护继承派生类 protected 成员;

基类的 private 成员保护继承派生类中不可访问。

例 5.4 保护继承的访问规则。

Page 17: 第 5 章  派生类与继承

#include<iostream.h>

class A{ // 声明一个基类

public:

int z;

void setA(int i){x=i; }

int getA(){ return x;}

private:

int x;

protected:

int y;

};

class B:protected A{

public:

int a;

void setB(int i,int j,int k,int l,int m,int n)

{ x=i; // 非法,在派生类 B 中 x 为不可访问成员应该是 setA(i)

y=j; // 合法,在派生类 B 中 y 是 protected 成员

z=k; // 合法,在派生类 B 中 z 是 protected 成员

a=l; // 合法,访问自己的公有成员

b=m;// 合法,访问自己的私有成员

c=n; // 合法,访问自己的保护成员

}

Page 18: 第 5 章  派生类与继承

void showB()

{cout<<”x=”<<x<<endl;// 非法,在派生类中 x 不可访问 cout<<”x=”<<getA()<<endl;// 合法, getA() 在 B 中是 protected 成员 cout<<”y=”<<y<<endl; // 合法, y 在 B 中是 protected 成员 cout<<”z=”<<z<<endl; // 合法, z 在 B 中是 protected 成员 cout<<”a=”<<a<<endl;

cout<<”b=”<<b<<endl;

cout<<”c=”<<c<<endl;}

private:

int b;

protected:

int c;};

main()

{ B obj;

obj.setB(10,20,30,40,50,60);

obj.showB();

cout<<”a=”<<obj.a<<endl; // 合法, c 在 B 中是 public 成员 }

Page 19: 第 5 章  派生类与继承

表 5-3 保护继承的访问规则

基类成员 private 成员 public 成员 protected 成员

内部访问 对象访问 

不可访问不可访问

可访问不可访问

可访问不可访问

Page 20: 第 5 章  派生类与继承

5.2.4 基类成员在派生类中的访问属性在基类中的访问属性 继承方式 在派生类中的访问属性

private public 不可直接访问

private private 不可直接访问

private protected 不可直接访问

public public public

public private private

public protected protected

protected public protected

protected private private

protected protected protected

Page 21: 第 5 章  派生类与继承

5.3 派生类的构造函数和析构函数 5.3.1 派生类构造函数 派生类构造函数和析构函数的执行顺序: 通常情况下 , 当创建派生类对象时 , 首先执行基类的构造函数 , 随后再执行派生类的构造函数 ;

当撤消派生类对象时 , 则先执行派生类的析构函数 , 随后再执行基类的析构函数。

Page 22: 第 5 章  派生类与继承

例 5_5 基类和派生类的构造函数及析构函数的执行顺序。 #include<iostream.h> class Base{ public: Base(){ cout<<"Constructing base class\n"; } // 基类的构造函数 ~Base(){ cout<<"Destructing baes class\n"; } // 基类的析构函数 }; class Derive:public Base{ public: Derive(){cout<<"Constructing derived class\n";} // 派生类的构造函数 ~Derive(){cout<<"Destructing derived class\n";} // 派生类的析构函数 }; main()

{ Derive op; return 0; }

Page 23: 第 5 章  派生类与继承

派生类构造函数和析构函数的构造规则

在 C++ 中 , 派生类构造函数的一般格式为 :

派生类 :: 派生类名 ( 参数总表 ): 基类名 ( 参数表 )

{

// 派生类新增成员的初始化语句 }

注意:这是基类有构造函数且含有参数时使用

Page 24: 第 5 章  派生类与继承

例 5_6 当基类含有带参数的构造函数时 , 派生类构造函数的构造方法。 #include<iostream.h> class Base { public: Base(int n) // 基类的构造函数

{ cout<<"Constructing base class\n"; i=n; } ~Base() // 基类的析构函数 { cout<<"Destructing base class\n"; } void showi() { cout<<i<<endl; }

private: int i; };

Page 25: 第 5 章  派生类与继承

class Derive :public Base{ public: Derive(int n,int m):Base(m) // 定义派生类构造函数时 , { // 缀上基类的构造函数

cout<<"Constructing derived class"<<endl; j=n; } ~Derive() // 派生类的析构函数 { cout<<"Destructing derived class"<<endl; } void showj(){ cout<<j<<endl;} private: int j;};main(){ Derive obj(50,60); obj.showi(); obj.showj(); return 0; }

Page 26: 第 5 章  派生类与继承

当派生类中含有内嵌对象成员时 , 其构造函数的一般形式为 :

派生类名 (参数总表 ): 基类名 (参数表 1),内嵌对象名 1( 内嵌对象参数表 1),…, 内嵌对象名 n( 内嵌对象参数表 n) { // 派生类新增成员的初始化语句 } 例 5.6

Page 27: 第 5 章  派生类与继承

#include<iostream.h>

class A // 声明类{

public:

A(int i) // 类 A 的构造函数{

a=i;

cout<<"A Constructor "<<a<<endl;

}

void dispA()

{

cout<<"a="<<a<<endl;

}

private:

int a;

};

Page 28: 第 5 章  派生类与继承

class B // 声明基类{

public:

B(int j):obj1(j+10) // 基类构造函数,缀上其对象成员构造函数

{

b=j;

cout<<"B Constructor"<<endl;

}

void dispB()

{

cout<<"b="<<b<<endl;

}

private:

int b;

A obj1;

};

Page 29: 第 5 章  派生类与继承

class C:public B //声明派生类,公有继承 B类{

public:

C(int k):B(k-2),obj(k+2) //派生类的构造函数,缀上基类构造函数和对象成员构造函数{ c=k;

cout<<"C Constructor"<<endl; }

void dispC()

{

obj.dispA(); //合法,调用自己对象成员的 public成员函数dispB(); //合法,调用基类的函数,继承后属性是 public

cout<<"c="<<c<<endl;}

private:

int c;

A obj; //类 A的对象,作为派生类的对象成员};

void main()

{ C c(2); c.dispC();}

Page 30: 第 5 章  派生类与继承

在定义派生类对象时 , 构造函数的执行顺序如下 : · 调用基类的构造函数(有多个基类时 ,调用顺序由它们在类中声明的顺序确定) ; · 调用内嵌对象成员的构造函数(有多个对象成员时 ,调用顺序由它们在类中声明的顺序确定) ; · 派生类的构造函数体中的内容 撤消对象时 , 析构函数的调用顺序与构造函数的调用顺序正好相反。

Page 31: 第 5 章  派生类与继承

5.3.2 派生类析构函数 ( 1 )当派生类中不含对象成员时● 在创建派生类对象时 , 构造函数的执行顺序是:基类的构造函数→派生类的构造函数;● 在撤消派生类对象时 , 析构函数的执行顺序是:派生类的析构函数→基类的析构函数。( 2 )当派生类中含有对象成员时● 在定义派生类对象时,构造函数的执行顺序:基类的构造函数→对象成员的构造函数→派生类的构造函数;● 在撤消派生类对象时,析构函数的执行顺序:派生类的析构函数→对象成员的析构函数→基类的析构函数。例 5.7

Page 32: 第 5 章  派生类与继承

5.4 多继承 派生类只有一个基类 , 这种派生方法称为单基派生或单继承。

当一个派生类具有多个基类时 , 这种派生方法称为多基派生或多继承。

Page 33: 第 5 章  派生类与继承

5.4.1 多继承的声明 有两个以上基类的派生类声明的一般形式如下 :

class 派生类名 : 继承方式 1 基类名 1,…, 继承方式 n 基类名 n{

// 派生类新增的数据成员和成员函数 };

例 5.8

Page 34: 第 5 章  派生类与继承

#include<iostream.h>class A // 声明基类{ private:

int x; public:

void setA(int i){x=i;}void showA(){ cout<<"x="<<x<<endl;}

};class B // 声明基类{ private:

int y; public:

void setB(int j){y=j;}void showB(){ cout<<“y=”<<y<<endl; }

};class C:public A,private B // 声明派生类

C ,公有继承 A 私有继承 B{ private:

int z; public:

void setC(int i,int j,int k){ z=k;setA(i);setB(j);}void showC(){ showA();showB();

cout<<"z="<<z<<endl;}

};void main(){ C obj;

obj.setA(1); // 合法, setA() 在派生类 C 中是 public 成员obj.showA(); // 合法, showA() 在派生类 C 中是 publi 成员obj.setC(1,2,3);obj.showC();//obj.setB(5); // 非法, setB() 在派生类 C 中是 private 成员//obj.showB(); // 非法, showB() 在派生类 C 中是 private 成员

}

Page 35: 第 5 章  派生类与继承

5.4.2 多继承的构造函数与析构函数

多继承构造函数定义的一般形式如下 :

派生类名 ( 参数总表 ): 基类名 1( 参数表 1),基类名 2( 参数表 2),…, 基类名 n( 参数表 n)

{

// 派生类新增成员的初始化语句 }

例 5.9

Page 36: 第 5 章  派生类与继承

#include<iostream.h>class A1{private: int a1;public: A1(int i) { a1=i; cout<<“A1 Constructor:”<<a1<<endl;

} ~A1() { cout<<“A1 Destructor”<<endl; }};class A2{private:

int a2;public:

A2(int j){ a2=j; cout<<"A2 Constructor:"<<a2<<endl; }~A2(){ cout<<“A2 Destructor”<<endl; }

};

class A3

{private:

int a3;

public:

A3(int k){a3=k;

cout<<"A3 Constructor:"<<a3<<endl;}

~A3(){

cout<<"A3 Destructor"<<endl;}

};

class D:public A1,public A2

{private:

int d;

A3 obj;

public:

D(int i,int j,int k,int l):A2(i),A1(j),obj(k)

{d=l;

cout<<"D Constructor:"<<d<<endl;}

~D(){

cout<<"D Destructor"<<endl;}

};

void main()

{ D dd(1,2,3,4); }

Page 37: 第 5 章  派生类与继承

上堂课回顾 1. 派生类构造函数 * 派生类构造函数构造规则。 * 派生类中含有内嵌对象时构造规则。2. 派生类析构函数 * 基类构造函数不被继承(自动执行)。 * 与构造函数执行顺序相反。3. 多继承 * 概念 * 声明 * 多继承的构造函数和析构函数

Page 38: 第 5 章  派生类与继承

5.4.3 派生类重载基类成员和二义性问题 派生类的成员包括从基类继承来的和自己新增加的成员。可以对基类继承来的成员加以重新定义,这种派生类与基类有同名成员的情况,就是派生类重载基类成员。 说明: ( 1 )经过继承所得成员,如果派生类没有重新定义,那么它会使用基类的成员。 ( 2 )如果派生类重载基类成员,那么就会掩盖基类的同名成员,改用派生类的新版本。如果要使用基类成员,需要采用以下形式: 基类名 :: 成员名 例 5.10

Page 39: 第 5 章  派生类与继承

#include<iostream.h>class animal{ // 基类 animalpublic:

void call(){cout<<"o…!o…!"<<endl;}void eat(){cout<<"I eat food."<<endl;}

};class dog:public animal // 声明派生类 do

g{public:

void call() // 重载基类 call() 函数{cout<<"won won"<<endl;}

};class cat:public animal // 声明派生类 cat{public:

void call() // 重载基类 call() 函数{cout<<"meow meow"<<endl;}

};

void main()

{

animal a;dog b;cat c;

a.call();

b.call(); // 调用 dog 类的 call() 函数 c.call(); // 调用 cat 类的 call() 函数 b.animal::call();// 调用基类的 call() 函

数 c.animal::call();// 调用基类的 call() 函数 b.eat(); // 调用基类的 eat() 函数 c.eat(); // 调用基类的 eat() 函数}

Page 40: 第 5 章  派生类与继承

5.4.3 派生类重载基类成员和二义性问题

1. 成员函数二义性问题

例 5.11

A(show()) B(show())

C

Page 41: 第 5 章  派生类与继承

#include<iostream.h>class A{protected:

int i;public:

void show(){cout<<"i="<<i<<endl;}

};class B{protected:

int j;public:

void show(){cout<<"j="<<j<<endl;}

};class C:public A,public B{public:

void set(int x,int y){i=x;j=y;}

};

void main()

{

C obj;

obj.set(1,2);

//obj.show();

obj.A::show();

obj.B::show();

}

Page 42: 第 5 章  派生类与继承

5.4.3 派生类重载基类成员和二义性问题

2. 成员变量二义性问题

A— 称为派生类 D 的公共基类

例 5.12

B C

D

A A

Page 43: 第 5 章  派生类与继承

#include<iostream.h>class A // 公共基类 A{public:

int b;void display(){cout<<"Display A"<<endl;}

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

int b1;};class C:public A // 直接基类 C{public:

int b2;};class D:public B,public C // 派生类 D{public:

int d;};

void main()

{

D obj;

//obj.b=5; // 二义性错误,编译器无法确定是哪一个 b

obj.C::b=5;// 从 C 继承的 b

obj.B::b=6;// 从 B 继承的 b

obj.b1=7;

obj.b2=8;

obj.d=9;

//obj.display(); // 二义性错误,无法确定是哪一个 display()

obj.C::display();// 从 C 类继承的display()

obj.B::display();// 从 B 类继承的 display()

}

Page 44: 第 5 章  派生类与继承

5.5 虚基类 1 、为什么引入虚基类 虽然也可以通过在成员变量名前面加上“类名 ::” 消除其二义性,但解决这个问题的最好方法是使用虚基类。虚基类方法可以保证在任何一个存在公共基类的派生类中,不会存在一个以上的同名成员变量。

Page 45: 第 5 章  派生类与继承

例 5.13 没有使用虚基类的例子。#include <iostream.h> class base { public: base(){ a=5; cout<<"base a="<<a<<endl; } protected: int a;};class base1:public base{ public: base1() { a=a+10; cout<<"base1 a="<<a<<endl; } };class base2:public base{ public: base2(){a=a+20; cout<<"base2 a="<<a<<endl;} };

Page 46: 第 5 章  派生类与继承

class derived:public base1,public base2{ public: derived() { cout<<"base1::a="<<base1::a<<endl; cout<<"base2::a="<<base2::a<<endl; } };main(){ derived obj; return 0; } 程序运行结果如下: :base a=5base1 a=15base a=5base2 a=25base1::a=15base2::a=25

Page 47: 第 5 章  派生类与继承

base base

base1 base2

 

derived

图 5-4 非虚基类的类层次图

Page 48: 第 5 章  派生类与继承

2. 虚基类的概念 虚基类:在一个类层次中,如果某个派生类存在一个公共基类,将这个基类设置为虚基类,这时从不同的路径继承过来的该类在内存中保留一个副本。 注意: ( 1 )虚基类并不是在声明基类是声明的,因为它可能做虚基类,可能不做。 ( 2 )为了保证虚基类在派生类中继承一次,应当在该基类的所有直接派生类中声明为虚基类。

Page 49: 第 5 章  派生类与继承

2. 虚基类的概念 虚基类的声明是在派生类的声明过程 ,

其语法形式如下 :

class 派生类名 :virtual 继承方式 类名 {

//…

}

为什么不在基类中使用 virtual ?

Page 50: 第 5 章  派生类与继承

例 5_13 虚基类的使用。#include <iostream.h> class base { public: base( ){ a=5; cout<<"base a="<<a<<endl;} protected: int a;};class base1: virtual public base{ public: base1( ){ a=a+10; cout<<"base1 a="<<a<<endl;} };class base2: virtual public base{ public: base2( ){ a=a+20; cout<<"base2 a="<<a<<endl;} };

Page 51: 第 5 章  派生类与继承

class derived:public base1,public base2{ public: derived( ){ cout<<"derived a="<<a<<endl;} };main( ){ derived obj; return 0; }程序运行结果如下: :base a=5base1 a=15base2 a=35derived a=35

Page 52: 第 5 章  派生类与继承

base base1 base2  derived 图 5-5 虚基类的类层次图

Page 53: 第 5 章  派生类与继承

3. 虚基类的初始化 在使用虚基类机制时应该注意以下几点 :

(1) 如果虚基类只定义一个带形参的构造函数 , 则所有直接或间接的派生类的构造函数都必须对虚基类进行初始化。

例 5_15

Page 54: 第 5 章  派生类与继承

(2) 虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的。

例 5_15 构造函数调用顺序为: A()B()

D()->C()E()->F() 。

Page 55: 第 5 章  派生类与继承

(3) 若同一层次中同时包含虚基类和非虚基类 , 应先调用虚基类的构造函数 , 再调用非虚基类的构造函数 , 最后调用派生类构造函数 ;

例 5_16

Page 56: 第 5 章  派生类与继承

(4)  对于多个虚基类 , 构造函数的执行顺序仍然是先左后右 ,自上而下 ;

(5)  对于非虚基类 , 构造函数的执行顺序仍是先左后右 , 自上而下 ;

例 5_17

Page 57: 第 5 章  派生类与继承

(6) 若虚基类由非虚基类派生而来 , 则仍然先调用基类构造函数 , 再调用派生类的构造函数。 class A{A(){…} …};

class B:public A { B(){…} …};

class C1:virtual public B {C1(){…} …};

class C2:virtual public B {C2(){…} …};

class D:public C1,public C2 {D(){…} …};

D d;

定义类 D 的对象 d 后,构造函数执行的顺序是:A()B()C1()C2()D() 。 4 、虚基类应用 例 5.14

Page 58: 第 5 章  派生类与继承

4 、虚基类应用 例 5.14

Page 59: 第 5 章  派生类与继承

5.6 赋值兼容规则

赋值兼容:不同类型数据之间的自动转换和赋值。 所谓对象赋值兼容规则是指在需要基类对象的任何地方都可以使用公有派生类的对象来替代。这样 , 公有派生类实际上就具备了基类的所有特性 ,凡基类能解决的问题 , 公有派生类也能解决。

Page 60: 第 5 章  派生类与继承

例如 , 下面声明的两个类 :

class Base{

};

class Derived:public Base{

};

根据赋值兼容规则 , 以下几种情况是合法的 :

Page 61: 第 5 章  派生类与继承

(1) 可以用派生类对象给基类对象赋值。例如 :

Base b;

Derived d;

b=d;

这样赋值的效果是 , 对象 b 中所有数据成员都将具有对象 d 中对应数据成员的值。

(2) 可以用派生类对象来初始化基类的引用。例如 :

Derived d;

Base &br=d;

Page 62: 第 5 章  派生类与继承

(3) 可以把派生类对象的地址赋值给指向基类的指针。例如 :

Derived d; Base *bptr=&d; 这种形式的转换 , 是在实际应用程序中

最常见到的。(4) 可以把指向派生类对象的指针赋值给指向基类对象的指针。例如 :

Derived *dptr,obj; dptr=&obj; Base *bptr=dptr;

Page 63: 第 5 章  派生类与继承

基类 B0 以公有方式派生出 B1 类, B1类再作为基类以公有方式派生出 D1 类,基类 B0 中定义了成员函数 display() ,在派生类中对这个成员函数进行了覆盖。 例 5.15

B0 类 void display() ;

B1 类 void display() ;

D1 类 void display() ;

Page 64: 第 5 章  派生类与继承

( 1 )声明为指向基类的指针可以指向它的公有派生类的对象,但不允许指向它的私有派生类的对象。例如:

class B {…} ; class D : private B {…} ; B b1 , *pbl ; D d1 ; pb1=&b1; // 合法,基类 B 的对象 b1 和 B 类的指针 pb1=&d1; // 非法,不允许将基类指针指向它的私有

派生类对象( 2 )允许将一个声明为指向基类的指针指向其公有派生类

对象,但是不能将一个声明为指向派生类对象的指针指向其基类的一个对象。

(3) 声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类的定义的成员。

Page 65: 第 5 章  派生类与继承

5.7 继承与组合 在一个类中以另一个类的对象作为数据成员的,称为类的组合。 例如,声明 Car 类(汽车)是 Vehicle类(车辆)的派生类,另外有一个类 Motor类(马达),我们可以将 Motor 类对象作为成员加到小汽车类中。如下:

Page 66: 第 5 章  派生类与继承

5.7 继承与组合 在一个类中以另一个类的对象作为数据

成员的,称为类的组合。 例如,声明 Car 类(汽车)是 Vehicle

类(车辆)的派生类,另外有一个类 Motor 类(马达),我们可以将 Motor 类对象作为成员加到小汽车类中。如下:

Page 67: 第 5 章  派生类与继承

class Vehicle { //...} ; class Motor { //...} ; class Car:public Vehicle { public : Motor motor ; } ; void vehicleFn(Vehiccle& v) ; void motorFn(Motor& m) ; void main() { Car c ; vehicleFn(c) ; // 合法 motorFn(c) ; // 非法 motorFn(c.motor) ; // 合法 }

Page 68: 第 5 章  派生类与继承

C++中“继承”太有用、太容易用,才要防止乱用“继承”。要给“继承”定一些使用规则:

( 1 )如果类 A 和类 B毫不相干,不可以为了使 B 的功能更多些而让 B 继承 A 的功能;

( 2 )如果类 B 有必要使用 A 的功能,则要分两种情况考虑。

Page 69: 第 5 章  派生类与继承

① 若在逻辑上 B 是 A 的“一种”( a kind of ),则允许 B 继承 A 的功能。 如男人( Man )是人( Human )的一种,男孩( Boy )是男人的一种。那么类 Man 可以从类 Human 派生,类 Boy 可以从类 Man 派生。示例程序如下: class Human { //… };

class Man : public Human { //… };

class Boy : public Man { //… };

Page 70: 第 5 章  派生类与继承

②若在逻辑上 A 是 B 的“一部分”( a part of ),则不允许 B 继承 A 的功能,而是要用A 和其它东西组合出 B 。 例如眼( Eye )、鼻( Nose )、口( Mouth )、耳( Ear )是头( Head )的一部分,所以类 Head 应该由类 Eye 、 Nose 、 Mouth 、Ear 组合而成,不是派生而成。示例程序如下:

Page 71: 第 5 章  派生类与继承

class Eye { public: void Look(void); };

class Nose { public: void Smell(void); };

class Mouth { public: void Eat(void); };

class Ear { public: void Listen(void); };

class Head

{

public:

void Look(void){ m_eye.Look(); }

void Smell(void){ m_nose.Smell(); }

void Eat(void){ m_mouth.Eat(); }

void Listen(void){ m_ear.Listen(); }

private:

Eye m_eye;

Nose m_nose;

Mouth m_mouth;

Ear m_ear;

};