第十章 继承和派生类

74
面面面面面面面面面 面面面面面面面面面面面面 面面面 《》 1 第第第 第第第第第第 第第第第第第第第 (C++)

description

第十章 继承和派生类. 面向对象程序设计 (C++). 10.1 引言. 10.1 引言 : 代码重用的两种机制. 组合:创建一个新类,类中的一个或多个数据成员是 (已经存在的)其他类的对象。 例:计算机 = 主板 +CPU+ 内存 + 显示器 + …. class CComputer{ Cpower Foxconn200; // 富士康 CMainBoard Intel915G;// Intel 主板 - PowerPoint PPT Presentation

Transcript of 第十章 继承和派生类

Page 1: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 1

第十章 继承和派生类

面向对象程序设计 (C++)

Page 2: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 2

10.1 引言

Page 3: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 3

10.1 引言 : 代码重用的两种机制 组合:创建一个新类,类中的一个或多个数据成员是 (已经存在的)其他类的对象。

例:计算机 = 主板 +CPU+内存 + 显示器 + …

class CComputer{ Cpower Foxconn200; // 富士康 CMainBoard Intel915G;// Intel 主板 CCPU Prescott; // Prescott 处理器 CMem SEC400X72C3 ; // Samsung 内存条 …};

Page 4: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 4

10.1 引言 : 代码重用的两种机制 继承:在现有的类(称为“基类” )的基础上,创 建一个新类(称为“派生类”)。派生类“共 享”基类的所有数据成员和成员函数;也可 以进一步添加自己的成员。 例: Dell C640DV 拥有 C640的所有配置;此外,还 增加了支持 DV的 i-link 1394接口。

class C640DV: public C640{ C1394 canopusDvRex ;}

Page 5: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 5

继承的例子:Base class Derived classes

Student GraduateStudent UndergraduateStudent

Shape Circle Triangle Rectangle

Loan(贷款) CarLoan(车贷) HomeImprovementLoan(房贷) MortgageLoan(抵押贷款)

Employee(雇员) FacultyMember(教员) StaffMember(职员)

Account CheckingAccount SavingsAccount

Page 6: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 6

10.1.1 组合

class X { int i; public: X() { i = 0; } void set(int ii) { i = ii; } int read() const { return i; } int permute() { return i = i * 47; } };

// new classclass Y { int i;

Page 7: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 7

10.1.1 组合 ( 续 )

public: X x; // Y 的成员是 X 的对象 Y() { i = 0; } void f(int ii) { i = ii; } int g() const { return i; }};

int main() { Y y; y.f(47); y.x.set(37); // 访问内嵌对象} ///:~

X::i

Y::i

Page 8: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 8

10.1.2 单继承class 派生类类名: public/private/pro… 基类

类名 { // 派生类的新成员;}

派生类继承基类的所有的数据成员和成员函数。除了:1. 基类的构造函数和析构函数;2. 基类中用户定义的 new和赋值运算符3. 基类的友元关系

访问修饰的不同,影响到派生类成员函数对基类成员的访问控制、派生类对象对基类成员的访问控制。

Page 9: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 9

eg1: 基类 employee

class employee {private:

char * name;short age;float salary;

public:employee(){ name=0; age=0; salary=0.0;}employee(char * name1, short age1, float

salary1) {

name = new char[strlen(name1)+1];

Page 10: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 10

eg1: 基类 employee

age=age1; salary=salary1; } void print() const{ cout<<"name:"<<name<<"age:"<<age <<"salary:"<<salary<<endl; } virtual ~employee() {

delete [ ]name; }}; //~ 基类 employee

Page 11: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 11

eg1: 派生类 manager1

name

age

salary

class manager1:public employee{ // 继承了基类的所有数据成员和成员函数。};

manager1 m;m.print(); // 打印 manager 的 name,age,salary

Page 12: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 12

10.1.3 多重继承 ( 其意义尚有争议 )

class manager{ …}; class temporary{ …};

// 类 consultant(顾问 )class consultant:public manager, public temporary{ … };

当基类中含同名成员时,存在二义性的问题!

Page 13: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 13

10.1.4 继承和组合的联合

class B { int i;public: B(int ii) : i(ii) { } ~B() { } void f() const { }};

class A { int i;public: A(int ii) : i(ii) { } ~A() { } void f() const { }};

Page 14: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 14

10.1.4 继承和组合的联合 ( 续 )

class C : public B { A a;public: C(int ii) : B(ii), a(ii) { } ~C() { } // Calls ~A() and ~B() void f() const { // Redefinition a.f(); B::f(); }};int main() { C c(47);} ///:~

Page 15: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 15

课堂作业: (10 分钟 )

class Derive:public Base{public: void d();};

class Member{ public: Base ba; void d();};

问 : Derive和 Member生成的对象是否具有相同的存储形式 ? 如果是,试从概念上、操作上,以及用途上比较两者之间的差异。

class Base {public: int i; void b();};

Page 16: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 16

组合 vs 继承1.概念: Member是组合 ,Derive是继承2.用途:代码重用3.操作: Base base; Derive derive; Member member; cout << base.i << derive.i << member.ba.i; base.b(); derive.b(); member.ba.b();

Page 17: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 17

组合 vs 继承 ( 续 )

“ Is a” relationships- Inheritance

Relationship in which a class is derived from another class

派生类“是一个”基类

“Has a” relationships- Composition

Relationship in which a class contains other classes as members

类中“有一个”其他类的成员对象

Page 18: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 18

10.2 继承的访问控制

Page 19: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 19

10.2.1 访问控制1. 基类对派生类的访问控制 2. 基类对派生类的对象的访问控制3. 基类对派生类的派生类的访问控制

class base{private: int i1;protected: int j1;private: int f1();

};class drv: public base{

private: int i2;protected: int j2;public: int f2();

};

void main(){ drv d1;}

问:如何访问 d1的数据成员和成员函数?

Page 20: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 20

公有继承

基类成员对派生类的可见性: --公有成员和保护成员可见,私有成员不可见。

基类成员对派生类对象的可见性 --公有成员可见,保护成员和私有成员不可见。

私有继承

基类成员对派生类的可见性: -- 公有成员和保护成员可见,私有成员不可见。 -- 所有成员对派生类的派生类的成员不可见 ( 即

转变为派生类的私有成员 ) 基类成员对派生类对象的可见性 -- 所有成员都不可见

Page 21: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 21

保护继承

基类成员对派生类的可见性: -- 公有成员和保护成员可见,私有成员不可见。 -- 公有成员和保护成员对派生类的派生类的成员可见。 基类成员对派生类对象的可见性 -- 所有成员都不可见

Page 22: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 22

10.2.2 public 继承class manager2:public employee{ public: int level; void m_print(){ cout << “level” << level ; // cout << age; error 不能访问基类私有成员; print(); //OK, 可以访问基类公有成员; }};

manager2 m;m.print(); //OK 打印 manager 的 ame,age,salary

Page 23: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 23

10.2.3 private 继承class manager3: private employee {public: int level; void m_print(){ cout << “level” << level ; //cout << age; 错!不能访问基类私有成员; print(); } };

manager2 m; m.print(); //manager3中的私有成员,不能访问

Page 24: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 24

10.2.3.1 对 private 继承成员公有化

class Pet {public: char eat() const { return 'a'; } int speak() const { return 2; } float sleep() const { return 3.0; } float sleep(int) const { return 4.0; }};

class Goldfish : Pet { // 私有继承public: Pet::eat; // 公有化 Pet::sleep; // 重载的成员均被公有化};

Page 25: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 25

10.2.3.1 对 private 继承成员公有化

int main() { Goldfish bob; bob.eat(); bob.sleep(); bob.sleep(1); //! bob.speak();// Error: private member function} ///:~

Page 26: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 26

10.2.4 protected 继承class Base { int i;protected: int read() const { return i; } void set(int ii) { i = ii; }public: Base(int ii = 0) : i(ii) { } int value(int m) const { return m*i; }};

class Derived : public Base { int j;

Page 27: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 27

10.2.4 protected 继承

注 :protected继承不常用,只是为了语言的完备性。

public: Derived(int jj = 0) : j(jj) {} void change(int x) { set(x); }};

int main() { Derived d; d.change(10); d.set(10); 错误,保护成员不能访问} ///:~

Page 28: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 28

10.2.5 小结 : 访问控制关系基类性质 继承性质 派生类对基

类可访问性基类成员在派生

类中性质派生类对象对基

类的可访问性派生类的派生类对基

类的可访问性

public public Yes public Yes   Yes

protected public Yes protected 不可访问 Yes

private public 不可访问 -- 不可访问 不可访问

publicprotecte

dYes protected 不可访问 Yes

protectedprotecte

dYes protected 不可访问 Yes

privateprotecte

d不可访问 -- 不可访问 不可访问

public private Yes private 不可访问 不可访问

protected private Yes private 不可访问 不可访问

private private 不可访问 -- 不可访问 不可访问

Page 29: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 29

public, private, and protected InheritanceType of inheritance Base class

member access specifier

public inheritance

protected inheritance

private

inheritance

Public

public in derived class. Can be accessed directly by any non-static member functions, friend functions and non-member functions.

protected in derived class.

Can be accessed directly by all non-static member functions and friend functions.

private in derived class.

Can be accessed directly by all non-static member functions and friend functions.

Protected

protected in derived class.

Can be accessed directly by all non-static member functions and friend functions.

protected in derived class.

Can be accessed directly by all non-static member functions and friend functions.

private in derived class.

Can be accessed directly by all non-static member functions and friend functions.

Private

Hidden in derived class.

Can be accessed by non-static member functions and friend functions through public or protected member functions of the base class.

Hidden in derived class.

Can be accessed by non-static member functions and friend functions through public or protected member functions of the base class.

Hidden in derived class.

Can be accessed by non-static member functions and friend functions through public or protected member functions of the base class.

Page 30: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 30

课堂作业:设计图形类

类的层次关系: shape shape point 或 circle point circle

cylinder cylinder

Page 31: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 31

图形类class shape { … } ; // 基类

class Point:public shape{ … }; // 点

class Circle:pulic point{ // 圆 double radius; ….;};

class Cylinder: public Circle{ // 圆柱体 double height; …. ;};

Page 32: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 32

1 // Fig. 9.8: point2.h2 // Definition of class Point3 #ifndef POINT2_H4 #define POINT2_H56 #include <iostream>78 using std::ostream;910 class Point {11 friend ostream &operator<<( ostream &, const Point & );12 public:13 Point( int = 0, int = 0 ); // default constructor14 void setPoint( int, int ); // set coordinates15 int getX() const { return x; } // get x coordinate16 int getY() const { return y; } // get y coordinate17 protected: // accessible to derived classes18 int x, y; // coordinates of the point19 };2021 #endif22 // Fig. 9.8: point2.cpp

23 // Member functions for class Point

24 #include "point2.h"

25

26 // Constructor for class Point

27 Point::Point( int a, int b ) { setPoint( a, b ); }

28

29 // Set the x and y coordinates

30 void Point::setPoint( int a, int b )

31 {

32 x = a;

Point data members are protected to be made accessible by Circle.

1. Point definition

1.1 Function definitions

Page 33: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 33

33 y = b;

34 }

35

36 // Output the Point

37 ostream &operator<<( ostream &output, const Point &p )

38 {

39 output << '[' << p.x << ", " << p.y << ']';

40

41 return output; // enables cascading

42 }

1.1 Function definitions

Page 34: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 34

1 // Fig. 9.9: circle2.h2 // Definition of class Circle3 #ifndef CIRCLE2_H4 #define CIRCLE2_H56 #include <iostream>78 using std::ostream;910 #include "point2.h"1112 class Circle : public Point {13 friend ostream &operator<<( ostream &, const Circle & );14 public:15 // default constructor16 Circle( double r = 0.0, int x = 0, int y = 0 );17 void setRadius( double ); // set radius18 double getRadius() const; // return radius19 double area() const; // calculate area20 protected: // accessible to derived classes21 double radius; // radius of the Circle22 };2324 #endif25 // Fig. 9.9: circle2.cpp

26 // Member function definitions for class Circle

27 #include <iomanip>

28

29 using std::ios;

30 using std::setiosflags;

31 using std::setprecision;

32

33 #include "circle2.h"

Circle data members are protected to be made accessible by Cylinder.

1. circle definition

1.1 Function definitions

Page 35: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 35

34

35 // Constructor for Circle calls constructor for Point

36 // with a member initializer and initializes radius

37 Circle::Circle( double r, int a, int b )

38 : Point( a, b ) // call base-class constructor

39 { setRadius( r ); }

40

41 // Set radius

42 void Circle::setRadius( double r )

43 { radius = ( r >= 0 ? r : 0 ); }

44

45 // Get radius

46 double Circle::getRadius() const { return radius; }

47

48 // Calculate area of Circle

49 double Circle::area() const

50 { return 3.14159 * radius * radius; }

51

52 // Output a circle in the form:

53 // Center = [x, y]; Radius = #.##

54 ostream &operator<<( ostream &output, const Circle &c )

55 {

56 output << "Center = " << static_cast< Point > ( c )

57 << "; Radius = "

58 << setiosflags( ios::fixed | ios::showpoint )

59 << setprecision( 2 ) << c.radius;

60

61 return output; // enables cascaded calls

62 }

1.1 Function definitions

Page 36: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 36

1 // Fig. 9.10: cylindr2.h

2 // Definition of class Cylinder

3 #ifndef CYLINDR2_H

4 #define CYLINDR2_H

5

6 #include <iostream>

7

8 using std::ostream;

9

10 #include "circle2.h"

11

12 class Cylinder : public Circle {

13 friend ostream &operator<<( ostream &, const Cylinder & );

14

15 public:

16 // default constructor

17 Cylinder( double h = 0.0, double r = 0.0,

18 int x = 0, int y = 0 );

19

20 void setHeight( double ); // set height

21 double getHeight() const; // return height

22 double area() const; // calculate and return area

23 double volume() const; // calculate and return volume

24

25 protected:

26 double height; // height of the Cylinder

27 };

28

29 #endif

1. Cylinder definition

Page 37: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 37

30 // Fig. 9.10: cylindr2.cpp

31 // Member and friend function definitions

32 // for class Cylinder.

33 #include "cylindr2.h"

34

35 // Cylinder constructor calls Circle constructor

36 Cylinder::Cylinder( double h, double r, int x, int y )

37 : Circle( r, x, y ) // call base-class constructor

38 { setHeight( h ); }

39

40 // Set height of Cylinder

41 void Cylinder::setHeight( double h )

42 { height = ( h >= 0 ? h : 0 ); }

43

44 // Get height of Cylinder

45 double Cylinder::getHeight() const { return height; }

46

47 // Calculate area of Cylinder (i.e., surface area)

48 double Cylinder::area() const

49 {

50 return 2 * Circle::area() +

51 2 * 3.14159 * radius * height;

52 }

53

54 // Calculate volume of Cylinder

55 double Cylinder::volume() const

56 { return Circle::area() * height; }

57

58 // Output Cylinder dimensions

59 ostream &operator<<( ostream &output, const Cylinder &c )

60 {

Circle::area() is overidden.

1.1 Function definitions

Page 38: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 38

61 output << static_cast< Circle >( c )

62 << "; Height = " << c.height;

63

64 return output; // enables cascaded calls

65 }

66 // Fig. 9.10: fig09_10.cpp

67 // Driver for class Cylinder

68 #include <iostream>

69

70 using std::cout;

71 using std::endl;

72

73 #include "point2.h"

74 #include "circle2.h"

75 #include "cylindr2.h"

76

77 int main()

78 {

79 // create Cylinder object

80 Cylinder cyl( 5.7, 2.5, 12, 23 );

81

82 // use get functions to display the Cylinder

83 cout << "X coordinate is " << cyl.getX()

84 << "\nY coordinate is " << cyl.getY()

85 << "\nRadius is " << cyl.getRadius()

86 << "\nHeight is " << cyl.getHeight() << "\n\n";

87

88 // use set functions to change the Cylinder's attributes

89 cyl.setHeight( 10 );

90 cyl.setRadius( 4.25 );

91 cyl.setPoint( 2, 2 );

X coordinate is 12

Y coordinate is 23

Radius is 2.5

Height is 5.7

1.1 Function definitions

----------------------1. Load headers

1.1 Initialize object

2. Function calls

2.1 Change attributes

3. Output data

Page 39: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 39

92 cout << "The new location, radius, and height of cyl are:\n"

93 << cyl << '\n';

94

95 cout << "The area of cyl is:\n"

96 << cyl.area() << '\n';

97

98 // display the Cylinder as a Point

99 Point &pRef = cyl; // pRef "thinks" it is a Point

100 cout << "\nCylinder printed as a Point is: "

101 << pRef << "\n\n";

102

103 // display the Cylinder as a Circle

104 Circle &circleRef = cyl; // circleRef thinks it is a Circle

105 cout << "Cylinder printed as a Circle is:\n" << circleRef

106 << "\nArea: " << circleRef.area() << endl;

107

108 return 0;

109}

X coordinate is 12Y coordinate is 23Radius is 2.5Height is 5.7 The new location, radius, and height of cyl are:Center = [2, 2]; Radius = 4.25; Height = 10.00The area of cyl is:380.53Cylinder printed as a Point is: [2, 2] Cylinder printed as a Circle is:Center = [2, 2]; Radius = 4.25Area: 56.74

The new location, radius, and height of cyl are:

Center = [2, 2]; Radius = 4.25; Height = 10.00

The area of cyl is:

380.53

Cylinder printed as a Point is: [2, 2]

pref "thinks" cyl is a Point, so it prints as one.

circleref "thinks" cyl is a Circle, so it prints as one.

Cylinder printed as a Circle is:

Center = [2, 2]; Radius = 4.25

Area: 56.74

3. Output data

Program Output

Page 40: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 40

10.3 继承与构造、析构函数

Page 41: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 41

10.3.1 继承与构造、析构函数基类中不能被继承的部分:1. 构造函数2. 析构函数3. 重载的 new 、赋值运算符4. 友员关系

但是,在派生类的构造函数和重载赋值运算符中可以调用基类的构造函数和重载赋值运算符。

Page 42: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 42

10.3.2 如何初始化基类成员? class manager: public employee{ int level;public: manager(char * name1, short age1, float,salary1,int level1) : employee(name1,age1,salary1){

level=level1; } void m_print(){

cout<<"salary:"<<salary<<"level"<<level<<endl;print();

}};

manager m(“Maggie”,20,6000,1);

Page 43: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 43

10.3.3 构造、析构函数的调用次序创建一个派生类的对象,调用次序为:1. 调用基类的构造函数 , 初始化基类成员;2. 调用数据成员对象的构造函数 ( 若有多个,则按类

中定义的先后次序调用 ) ,初始化成员对象;3. 调用派生类自己的构造函数;初始化派生类成员。

析构一个派生类的对象,其调用次序正好相反:1. 调用派生类自己的析构函数;2. 调用数据成员对象的析构函数 ( 若有多个,则按类

中定义的先后次序逆序调用 ) ;3. 调用基类的析构函数。

Page 44: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 44

10.4 继承与特殊成员函数

Page 45: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 45

10.4.1 继承与静态成员函数

静态成员函数可被继承到派生类中; 如果重新定义了一个静态成员,所有在基类

中的其他重载函数会被隐藏; 如果改变了基类中一个函数的签名

(signature),所有使用该函数名字的基类版本都将被隐藏。

与非静态成员函数的不同点:静态成员函数不可以是虚函数。

Page 46: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 46

10.5 继承与运算符重载函数(略)

Page 47: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 47

10.6 名字隐藏

Page 48: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 48

10.6.1 重定义 (redefining)

在派生类中,重新定义基类的成员函数。

// 基类class employee {public: short age; employee( ) { age=0;} employee( short age1){ age=age1;} void print() const{

cout“ base age:"<<age<<endl; }};

Page 49: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 49

// 派生类class manager: public employee{ public: int level;

short age;manager(short age1,int level1)

:employee(age1) { age = age1+1; level=level1; }

void print(){ cout<<“derive age:"<<age; employee::print(); }};

Page 50: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 50

// 主函数void main(){ manager m(24,1); employee &pref=m; pref.print() ; // 调用派生类的 print();}

Page 51: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 51

10.6.2 重写 (overriding)若被重新定义的基类函数是虚函数,则称派生类中同名函数为重写。 ( 注意:一组虚函数的参数和返回值必须相同。)

class shape{ … ; virtual void draw( ); // 基类中的虚函数 }

class point{ … void draw(); // overriding }class circle{ // … void draw(); // overriding}

Page 52: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 52

10.6.3 名字隐藏 (name hiding)

在重新定义基类的重载函数时 ( 可以改变参数或返回值 ) ,则基类中的所有其他版本的重载函数被自动隐藏了,对派生类的对象不可见。

class Base {public: int f() const { cout << "Base::f()\n"; return 0; } int f(string) const { return 1; }};

Page 53: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 53

class Derived1 : public Base {public: // Redefinition: int f() const { cout << "Derived1::f()\n"; return 1; }// int f(string) 对 Derived1 不可用};

class Derived2 : public Base {public: // Change return type: void f() const { cout << "Derived2::f()\n"; }};// int f() 和 int f(string) 对 Derived2 都不可用

Page 54: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 54

class Derived3 : public Base {public: // Change argument list: int f(int) const { cout << "Derived3::f()\n"; return 3; } // int f() 和 int f(string) 对 Derived3 都不可用};

int main() { string s("hello"); Derived1 d1; x = d1.f(); //! d1.f(s); // error ! string version hidden

Page 55: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 55

注意:对虚函数稍有不同,详见下章。

Derived2 d2; //! x = d2.f(); // error! return int version hidden // ! d2.f(s) // string version hidden //! d1.f(s); // string version hidden Derived2 d2; //! x = d2.f(); // return int version hidden // ! d2.f(s) // string version hidden Derived3 d3; //! x = d3.f(); // f() version hidden x = d3.f(1);} ///:~

Page 56: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 56

10.7 渐增式开发

Page 57: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 57

10.7.1 什么是“渐增式开发”?

假设你写好了一段代码 C1, C1没有任何 bug;现在你往代码 C1中添加新的代码 C2,扩充 C1的功能 , 如果能保证不会因为代码的扩充给 C1带来新的bug,也就是说,如果代码扩充后有 bug,则 bug只可能出现在 C2中。我们称这种代码开发的方式为“渐增式开发”。

Page 58: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 58

10.7.2 继承、组合与“渐增式开发”

继承和组合都支持“渐增式开发”。- 派生类中的 bug不可能影响到基类;- 新类中的 bug不可能影响到成员对象所属的类

Page 59: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 59

10.8 向上类型转换

Page 60: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 60

10.8.1 派生类和基类的关系 “是一个”的关系

- 派生类拥有所有的基类的数据成员和成员函数;- 派生类对象可以当成一个基类对象来看待;但反过来不

可以。 任何发送给基类的消息派生类都可以接收

继承首先是代码重用的一种机制;但另一个重要的特征在于:继承描述了一组类,这组类在一个相关联的“层次”上,其中,一个类可以看作是另一个类。

Page 61: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 61

10.8.2 向上类型转换 继承最重要的思想实际上是说:派生类同时也是一个基类。

-- 也就是说:你可以发送给基类的任何消息,同样可以发送给派生类!

编译器通过允许被称作“向上类型转换 (Upcast-ing)”的机制来继承的这一特征。

-- 详见下例

Page 62: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 62

参见教材第 346 页enum note { middleC, Csharp, Cflat }; // Etc.

class Instrument {public: void play(note) const {}};

// Wind objects are Instruments// because they have the same interface:class Wind : public Instrument { };

void tune(Instrument& i) { // ... i.play(middleC);}

由于派生类的对象同时也是基类的一个对象,因此, tune()函数对Instrument和从Instrument派生出来的任何类都是有效的。

Page 63: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 63

参见教材第 346 页

int main() { Wind flute; // Upcasting tune(flute); } ///:~

函数 tune的形参是 Instrument & ,因此,在函数调用时,实际上进行了一个转换,将 Wind的引用转换成Instrument的引用。

这种将派生类的引用或指针转换成基类引用或指针的活动称为“向上类型转换” ( Upcasting!)

Page 64: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 64

• 向上类型转换不仅仅发生在虚实结合时,例如: Wind w;

Instrument * ip = &w ; // upcasting

Instrument & ir = w ; // upcasting

• 向上类型转换总是安全的:– 从派生类接口转换到基类接口可能出现的唯一的事情是失去成员,而不是获得他们

• 编译器允许向上类型转换;既不需要显式的转换,也不需要其他特别的标记。

Page 65: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 65

10.8.4 upcasting 与拷贝构造函数// Correctly creating the copy-constructor#include <iostream>using namespace std;

class Parent { int i;public: Parent(int ii) : i(ii) { cout << "Parent(int ii)\n"; } Parent(const Parent& b) : i(b.i) { cout << "Parent(const Parent&)\n"; } Parent() : i(0) { cout << "Parent()\n"; }

Page 66: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 66

friend ostream& operator<<(ostream& os, const Parent& b) { return os << "Parent: " << b.i << endl; }};

class Member { int i;public: Member(int ii) : i(ii) { cout << "Member(int ii)\n"; } Member(const Member& m) : i(m.i) { cout << "Member(const Member&)\n"; } friend ostream& operator<<(ostream& os, const Member& m) { return os << "Member: " << m.i << endl;

Page 67: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 67

}};

class Child : public Parent { int i; Member m;public: Child(int ii) : Parent(ii), i(ii), m(ii) { cout << "Child(int ii)\n"; } friend ostream& operator<<(ostream& os, const Child& c){ return os << (Parent&)c << c.m << "Child: " << c.i << endl; }};

Page 68: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 68

int main() { Child c(2); cout << "calling copy-constructor: " << endl; Child c2 = c; // Calls copy-constructor cout << "values in c2:\n" << c2;} ///:~

程序输出:Parent(int ii)Member(int ii)Child(int ii)calling copy-constructor:Parent(const Parent&)Member(const Member&)values in c2:Parent: 2Member: 2Child: 2

Page 69: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 69

10.8.4 非显式类型转换 基类和派生类之间对象的赋值

- 派生类的类型和基类的类型并不相同- 派生类的对象可以当成基类对象来处理

派生类中包含基类中所有的成员 派生类中可能包含基类中没有的成员 派生类可以赋值给基类

- 基类对象不能当成派生类的对象来处理 会造成多出来的那些派生类成员未定义 基类不能赋值给派生类 但可以通过重载赋值运算符来允许这样的赋值

Page 70: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 70

10.8.4 非显式类型转换 ( 续 )

通过指针来引用对象- 通过基类指针引用基类对象

Allowed- 通过派生类指针引用派生类对象

Allowed- 通过基类指针引用派生类对象

可能会出现语法错误 代码中只能引用基类的成员,否则会有语法错误

- 通过派生类指针引用基类对象 隐式转换会造成语法错误 (Not Allowed) 否则,派生类指针必须先转换成基类指针(显式转换)

Page 71: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 71

10.8.5 向下类型转换

当基类指针 ( 或引用 ) 转换到派生类指针( 或引用 ) ,称作“向下类型转换”。 向下类型转换不安全,因此,必须显式地进行 ( 显式类型转换 ) 。

Page 72: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 72

10.8.6 向上类型转换的问题

向上类型转换会丢失对象的类型信息。

eg :Wind w;Instrument *iptr = &w; // upcastingiptr->play(middleC);

由于 iptr的静态类型 Instrument*; 在编译时该函数调用将被绑定到Instrument::play();而不是 Wind::play()!

如何解决:利用多态( polymorphism )!

Page 73: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 73

10.9 小结

继承和组合都允许由已存在的类型创建新类型;

如果想重用已有类的内部实现的话,最好用组合;如果想重用已有类的接口的话,就应使用继承。

继承描述的类之间的层次关系是多态的前提。

Page 74: 第十章  继承和派生类

《面向对象程序设计》 中国科学技术大学计算机系 马建辉 74

作业:

P351 14-2

P352 14-20