版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第八章第八章 多态性多态性C+C+语言程序设计语言程序设计本章主要内容本章主要内容l多态性的概念多态性的概念l运算符重载运算符重载l静态绑定与动态绑定静态绑定与动态绑定l虚函数虚函数l纯虚函数与抽象类纯虚函数与抽象类l小结与复习建议小结与复习建议8.1 多态性的概念多态性的概念l多态性是面向对象程序设计的重要特征之一。多态性是面向对象程序设计的重要特征之一。l多态性是指发出同样的消息被不同类型的对象接收多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为。时有可能导致完全不同的行为。l多态的实现多态的实现(重载、强制、包含和参数多态重载、强制、包含和参数多态): 函数重载函数
2、重载 运算符重载运算符重载 虚函数虚函数 函数模板和类模板函数模板和类模板强制多态是编译程序的一种。通过强制多态是编译程序的一种。通过语义操作,把操作对象的类型强行语义操作,把操作对象的类型强行加以变换,以符合函数或操作符的加以变换,以符合函数或操作符的要求。要求。 封装性:封装性:封装是面向对象方法的一个重要原则。封装是面向对象方法的一个重要原则。封装有两个含封装有两个含义义:结合性,结合性,即将属性和方法结合;即将属性和方法结合;信息隐蔽性,信息隐蔽性,利用接口机利用接口机制隐蔽内部细节。因此封装的目的在于将对象的使用者和设计制隐蔽内部细节。因此封装的目的在于将对象的使用者和设计者分开,以
3、提高软件的可维护性和可修改性。者分开,以提高软件的可维护性和可修改性。继承性:继承性:是类间的一种基本关系,是基于层次关系的不同类共是类间的一种基本关系,是基于层次关系的不同类共享数据和操作的一种机制。父类定义了其所有子类的公共属性享数据和操作的一种机制。父类定义了其所有子类的公共属性和操作,在子类中除了定义自己特有的属性和操作外,还可以和操作,在子类中除了定义自己特有的属性和操作外,还可以对父类(或祖先类)中的操作重新定义其实现方法,即对父类(或祖先类)中的操作重新定义其实现方法,即重载重载。多态性:多态性:是指同一操作作用于不同对象上可以有不同的解释,是指同一操作作用于不同对象上可以有不同
4、的解释,并产生不同的执行结果。并产生不同的执行结果。动态绑定动态绑定,是指在程序运行时才将消,是指在程序运行时才将消息所请求的操作与实现该操作的方法进行连接。息所请求的操作与实现该操作的方法进行连接。C+C+的封装性、继承性和多态性的概念的封装性、继承性和多态性的概念 多态指同一个实体同时具有多种形式。它是面向多态指同一个实体同时具有多种形式。它是面向对象程序设计(对象程序设计(OOP)的一个重要特征。)的一个重要特征。 C+中的多态性具体体现在运行和编译两个方面。中的多态性具体体现在运行和编译两个方面。运行时多态是运行时多态是动态多态动态多态,其具体引用的对象在运行时,其具体引用的对象在运行
5、时才能确定。编译时多态是才能确定。编译时多态是静态多态静态多态,在编译时就可以,在编译时就可以确定对象使用的形式。确定对象使用的形式。 面向对象技术的多态性反映了客观世界的多态性面向对象技术的多态性反映了客观世界的多态性。多态性体现在面向对象程序设计的许多方面,同。多态性体现在面向对象程序设计的许多方面,同时提供了设计的灵活性,因而也增强了类模块的通时提供了设计的灵活性,因而也增强了类模块的通用性。用性。多态:多态:同一操作作用于不同的对象,可以有不同的同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,
6、来调用实现派生类中的方法。指向基类的指针,来调用实现派生类中的方法。 面向对象的多态性从实现的角度来讲,可以分面向对象的多态性从实现的角度来讲,可以分为静态多态性和动态多态性两种。为静态多态性和动态多态性两种。 静态多态性是在编译的过程中确定同名操作的具体操静态多态性是在编译的过程中确定同名操作的具体操作对象的,而动态多态性则是在程序运行过程中动态地确作对象的,而动态多态性则是在程序运行过程中动态地确定操作所针对的具体对象的。定操作所针对的具体对象的。 这种确定操作具体对象的过程就是联编这种确定操作具体对象的过程就是联编(binding)(binding),也称为绑定。也称为绑定。联编是指计算
7、机程序自身彼此关联的过程。联编是指计算机程序自身彼此关联的过程。也就是把一个标识符名和一个存储地址联系在一起的过程也就是把一个标识符名和一个存储地址联系在一起的过程。用面向对象的术语讲,就是把一条消息和一个对象的方。用面向对象的术语讲,就是把一条消息和一个对象的方法相结合的过程。法相结合的过程。 关于包含多态的问题关于包含多态的问题l在多继承时,基类与派生类之间,或基类之间出在多继承时,基类与派生类之间,或基类之间出现同名成员时,将出现访问时的二义性(不确定现同名成员时,将出现访问时的二义性(不确定性)性)可采用可采用虚函数虚函数或或同名隐藏规则同名隐藏规则来解决。来解决。l当派生类从多个基类
8、派生,而这些基类又从同一当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,个基类派生,则在访问此共同基类中的成员时,将产生二义性将产生二义性采用采用虚基类虚基类来解决。来解决。多态性的概念多态性的概念l 所谓多态性是指同一个接口可以通过所谓多态性是指同一个接口可以通过多种方法调用,如下图所示。多种方法调用,如下图所示。多态性为用户提供单一接口示意图多态性为用户提供单一接口示意图9 函数重载和运算符重载就属于静态多态性函数重载和运算符重载就属于静态多态性。 虚函数就是通过动态联编完成的。虚函数就是通过动态联编完成的。 函数重载在函数及类的章节中曾做过详细的讨论函
9、数重载在函数及类的章节中曾做过详细的讨论,所以在本章中,静态多态性主要介绍运算符重载;,所以在本章中,静态多态性主要介绍运算符重载;对于动态多态性,将对虚函数作详细介绍。对于动态多态性,将对虚函数作详细介绍。 8.2 运算符重载运算符重载 C+中预定义的运算符的操作对象只能是基中预定义的运算符的操作对象只能是基本数据类型。实际上,对于很多用户自定义的本数据类型。实际上,对于很多用户自定义的类型类型(如类如类),也需要有类似的运算操作。,也需要有类似的运算操作。 如:如:point p1(1,1), p2(3,3) 如果我们需要对如果我们需要对p1和和p2进行加法运算,该进行加法运算,该如何实现
10、呢?我们当然希望能使用如何实现呢?我们当然希望能使用“+”运算符运算符,写出表达式,写出表达式“p1+p2”,但是编译的时候却会,但是编译的时候却会出错,因为编译器不知道该如何完成这个加法出错,因为编译器不知道该如何完成这个加法。l 运算符重载是对已有的运算符赋予多重含义,使运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用于不同类型的数据时,导致不同类同一个运算符作用于不同类型的数据时,导致不同类型的行为。型的行为。l 在运算符重载的实现过程中,首先把指定的运算在运算符重载的实现过程中,首先把指定的运算表达式转化为对运算符函数的调用,运算对象转化为表达式转化为对运算符函数的调用,运算
11、对象转化为运算符函数的实参,然后,根据实参的类型来确定需运算符函数的实参,然后,根据实参的类型来确定需要调用的函数。这个过程是在编译过程中完成的。要调用的函数。这个过程是在编译过程中完成的。运算符重载的实质运算符重载的实质l运算符重载是对已有的运算符赋予多重含义运算符重载是对已有的运算符赋予多重含义l必要性必要性C+C+中预定义的运算符其运算对象只能是基中预定义的运算符其运算对象只能是基本数据类型,而不适用于用户自定义类型本数据类型,而不适用于用户自定义类型(如类)(如类)l实现机制实现机制 将指定的运算表达式转化为对运算符函数的将指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符
12、函数的实参。调用,运算对象转化为运算符函数的实参。 编译系统对重载运算符的选择,遵循函数重编译系统对重载运算符的选择,遵循函数重载的选择原则。载的选择原则。问题举例问题举例复数的运算复数的运算class Complex class Complex /复数类声明复数类声明public:public:Complex(double r = 0.0,double i = 0.0) Complex(double r = 0.0,double i = 0.0) real = r; imag=i; real = r; imag=i; void display() const;void display() c
13、onst;/显示复数的值显示复数的值private:private:double real;double real;double imag;double imag;15l用用“+”、“-”能够实现复数的加减运能够实现复数的加减运算算.l实现复数加减运算的方法实现复数加减运算的方法 重载重载“+”、“-”运算符运算符规则和限制规则和限制lC+中能够被重载的运算符有:中能够被重载的运算符有:new delete new delete + - * / % & | ! = += -= *= /= %= &= |= = = = != & | + - , -* -() 16l只能重
14、载只能重载C+语言中已有的运算符,不语言中已有的运算符,不可臆造新的。可臆造新的。l不改变原运算符的优先级和结合性。不改变原运算符的优先级和结合性。l不能改变操作数个数。不能改变操作数个数。l经重载的运算符,其操作数中至少应该经重载的运算符,其操作数中至少应该有一个是自定义类型。有一个是自定义类型。两种形式两种形式l重载为类的非静态成员函数重载为类的非静态成员函数l重载为非成员函数重载为非成员函数( (为类的友元函数为类的友元函数) ) 重载为类的非静态成员函数时:重载为类的非静态成员函数时:l声明形式声明形式函数类型函数类型 operator 运算符(形参)运算符(形参) . Complex
15、 operator + (Complex c2) ;Complex operator + (Complex c2) ; /运算符运算符+ +重重载成员函数载成员函数Complex operator - (Complex c2) ;Complex operator - (Complex c2) ; /运算符运算符- -重重载成员函数载成员函数20重载为非成员函数重载为非成员函数( (为类的友元函数为类的友元函数) ) 时:时:l声明形式声明形式lFriend 函数类型 operator 运算符(形参) .friend Complex operator + (Complex c1, Complex
16、 c2); /运算符运算符+ +重载重载friend Complex operator - (Complex c1, Complex c2); /运算符运算符- -重载重载l声明形式声明形式函数类型函数类型 operator 运算符(形参)运算符(形参) .l重载为类成员函数时重载为类成员函数时 参数个数参数个数=原操作数个数原操作数个数-1 (后置(后置+、-除外)除外)l重载为非成员函数时重载为非成员函数时 参数个数参数个数=原操作数个数原操作数个数,且至少应该有一个自定义类型的形参。且至少应该有一个自定义类型的形参。运算符成员函数的设计运算符成员函数的设计l双目运算符双目运算符 B B
17、如果要重载如果要重载 B 为类成员函数,使之能够实现表达为类成员函数,使之能够实现表达式式 oprd1 B oprd2,其中,其中 oprd1 为为A 类对象,则类对象,则 B 应被重载为应被重载为 A 类的成员函数,形参类型应该是类的成员函数,形参类型应该是 oprd2 所属的类型。所属的类型。 经重载后,表达式经重载后,表达式 oprd1 B oprd2 相当于相当于 oprd1.operator B(oprd2)l 前置单目运算符前置单目运算符 U U(如:(如:+和和-) 例例 8-1 将将“+”+”、“-”-”运算重载为复数类运算重载为复数类的成员函数。的成员函数。l 规则规则: 实
18、部和虚部分别相加减。实部和虚部分别相加减。l 操作数操作数: 两个操作数都是复数类的对象。两个操作数都是复数类的对象。#include #include using namespace std;using namespace std;class Complex class Complex /复数类定义复数类定义public:public:/外部接口外部接口Complex(double r = 0.0, double i = 0.0) : Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) real(r), imag(i) /构造
19、函数构造函数Complex operator + (Complex c2) ;/运算符运算符+ +重重载成员函数载成员函数Complex operator - (Complex c2) ;/运算符运算符- -重重载成员函数载成员函数void display();void display();/输出复数输出复数private:private:/私有数据成员私有数据成员double real;double real;/复数实部复数实部double imag;double imag;/复数虚部复数虚部;24Complex Complex:operator + (Complex c2) Complex
20、 Complex:operator + (Complex c2) /重载运算符函数实现重载运算符函数实现return return Complex(real + c2.real, imag + c2.imag); / /创建一个临时无名对象作为返回值创建一个临时无名对象作为返回值 Complex Complex:operator - (Complex c2) Complex Complex:operator - (Complex c2) /重载运算符函数实现重载运算符函数实现return return Complex(real - c2.real, imag - c2.imag); / /创建
21、一个临时无名对象作为返回值创建一个临时无名对象作为返回值 25void Complex:display() const void Complex:display() const cout ( real , imag ) cout ( real , imag ) endl;endl; int main() int main() /主函数主函数Complex c1(5, 4), c2(2, 10), c3Complex c1(5, 4), c2(2, 10), c3;/;/定义复数类的对定义复数类的对象象cout c1 = ; c1.display();cout c1 = ; c1.display
22、();cout c2 = ; c2.display();cout c2 = ; c2.display();c3 = c3 = c1 - c2; ;/使用重载运算符完成复数减法使用重载运算符完成复数减法cout c3 = c1 - c2 = ; c3.display();cout c3 = c1 - c2 = ; c3.display();c3 = c3 = c1 + c2; ;/使用重载运算符完成复数加法使用重载运算符完成复数加法cout c3 = c1 + c2 = ; c3.display();cout c3 = c1 + c2 = ; c3.display();return 0;retu
23、rn 0; 26程序输出的结果为:程序输出的结果为:c1 = (5, 4)c1 = (5, 4)c2 = (2, 10)c2 = (2, 10)c3 = c1 - c2 = (3, -6)c3 = c1 - c2 = (3, -6)c3 = c1 + c2 = (7, 14)c3 = c1 + c2 = (7, 14)前置单目运算符前置单目运算符 U(U(如:如:+和和-) -) 如如+i+il如果要重载如果要重载 U 为类成员函数,使之能够实现为类成员函数,使之能够实现表达式表达式 U oprd,其中,其中 oprd 为为A类对象,类对象,则则 U 应被重载为应被重载为 A 类的成员函数,无
24、形参。类的成员函数,无形参。l经重载后,经重载后,表达式表达式 U oprd 相当于相当于 oprd.operator U() l如:如:Clock & operator + (); /Clock & operator + (); /前置单目前置单目运算符重载运算符重载后置单目运算符后置单目运算符 +和和- - 如如+i+il如果要重载如果要重载 +或或-为类成员函数,使之能为类成员函数,使之能够实现表达式够实现表达式 oprd+ 或或 oprd- ,其中,其中 oprd 为为A类对象,则类对象,则 +或或- 应被重载为应被重载为 A 类的成员函数,且具有一个类的成员函数,且具
25、有一个 int 类型类型形参形参。l经重载后,表达式经重载后,表达式 oprd+ 相当于相当于 oprd.operator +(0)lClock operator + (int);/后置单目运算符后置单目运算符重载重载例例8-2l运算符前置运算符前置+和后置和后置+重载为时钟类重载为时钟类的成员函数。的成员函数。l前置单目运算符,重载函数没有形参,前置单目运算符,重载函数没有形参,对于后置单目运算符,重载函数需要有对于后置单目运算符,重载函数需要有一个整型形参。一个整型形参。l操作数是时钟类的对象。操作数是时钟类的对象。l实现时间增加实现时间增加1秒钟。秒钟。#include #include
26、 using namespace std;using namespace std;class Clock class Clock /时钟类声明定义时钟类声明定义public:public:/外部接口外部接口Clock(int hour = 0, int minute = 0, int Clock(int hour = 0, int minute = 0, int second = 0);second = 0);void showTime() const;void showTime() const;Clock &operator + (); /前置单目运算符重前置单目运算符重载载Cloc
27、k operator + (int);/后置单目运算符重后置单目运算符重载载private:private:/私有数据成员私有数据成员int hour, minute, second;int hour, minute, second;31/前置单目运算符重载函数前置单目运算符重载函数Clock & Clock:operator + () second+;second+;if (second = 60) if (second = 60) second -= 60;second -= 60;minute+;minute+;if (minute = 60) if (minute = 60)
28、minute -= 60;minute -= 60;hour = (hour + 1) % 24;hour = (hour + 1) % 24; return return * *this;this; 32/后置单目运算符重载后置单目运算符重载Clock Clock:operator + (int) /注意形参表中的整型参数注意形参表中的整型参数Clock old = Clock old = * *this;this;+(+(* *this);this);/调用前置调用前置“+”+”运算符运算符return old;return old; 33/其它成员函数的实现略其它成员函数的实现略int
29、main() int main() Clock myClock(23, 59, 59);Clock myClock(23, 59, 59);cout First time output: ;cout First time output: ;myClock.showTime();myClock.showTime();cout Show myClock+: ;cout Show myClock+: ;( (myClock+).showTime();).showTime();cout Show +myClock: ;cout Show +myClock: ;( (+myClock).showTime
30、();).showTime();return 0;return 0; 程序运行结果为:程序运行结果为:First time output: 23:59:59First time output: 23:59:59Show myClock+: 23:59:59Show myClock+: 23:59:59Show +myClock: 0:0:1Show +myClock: 0:0:1运算符非成员运算符非成员(友元友元)函数的设计函数的设计l函数的形参代表依自左至右次序排列的各操函数的形参代表依自左至右次序排列的各操作数。作数。l后置单目运算符后置单目运算符 +和和-的重载函数,形参列的重载函数,形
31、参列表中要增加一个表中要增加一个intint,但不必写形参名。,但不必写形参名。l如果在运算符的重载函数中需要操作某类对如果在运算符的重载函数中需要操作某类对象的私有成员,可以将此函数声明为该类的象的私有成员,可以将此函数声明为该类的友元。友元。36l双目运算符双目运算符 B重载后,重载后,表达式表达式oprd1 B oprd2 等同于等同于operator B(oprd1,oprd2 )l前置单目运算符前置单目运算符 B重载后,重载后,表达式表达式 B oprd 等同于等同于operator B(oprd )l后置单目运算符后置单目运算符 +和和-重载后,重载后,表达式表达式 oprd B
32、等同于等同于operator B(oprd,0 )37例例8-3l将将+ +、- -(双目)重载为非成员函数,(双目)重载为非成员函数,并将其声明为复数类的友元,两个操并将其声明为复数类的友元,两个操作数都是复数类的常引用。作数都是复数类的常引用。#include #include using namespace std;using namespace std;class Complex class Complex /复数类定义复数类定义public:public:/外部接口外部接口Complex(double r = 0.0, double i = 0.0) : real(r), Compl
33、ex(double r = 0.0, double i = 0.0) : real(r), imag(i) imag(i) /构造函数构造函数friend Complex operator + (Complex c1, Complex c2); /运算符运算符+ +重载重载friend Complex operator - (Complex c1, Complex c2); /运算符运算符- -重载重载private:private:/私有数据成员私有数据成员double real;double real;/复数实部复数实部double imag;double imag;/复数虚部复数虚部;3
34、8Complex Complex operator + (Complex c1, Complex c2) (Complex c1, Complex c2) return Complex(c1.real + c2.real, c1.imag + c2.imag); return Complex(c1.real + c2.real, c1.imag + c2.imag); Complex Complex operator - (Complex c1, Complex c2) (Complex c1, Complex c2) return Complex(c1.real - c2.real, c1.
35、imag - c2.imag); return Complex(c1.real - c2.real, c1.imag - c2.imag); 8.3 静态绑定与动态绑定静态绑定与动态绑定l绑定绑定 程序自身彼此关联的过程,确定程序中的程序自身彼此关联的过程,确定程序中的操作调用与执行该操作的代码间的关系。操作调用与执行该操作的代码间的关系。l静态绑定静态绑定 绑定过程出现在编译阶段,用对象名或者绑定过程出现在编译阶段,用对象名或者类名来限定要调用的函数。类名来限定要调用的函数。l动态绑定动态绑定 绑定工作在程序运行时执行,在程序运行绑定工作在程序运行时执行,在程序运行时才确定将要调用的函数。时
36、才确定将要调用的函数。#include#includeusing namespace std;using namespace std;class class PointPoint public:public:Point(double x, double y) : x(x), y(y) Point(double x, double y) : x(x), y(y) doubledouble areaarea() const return 0.0; () const return 0.0; private:private:double x, y;double x, y;class class Rec
37、tangleRectangle: public : public PointPoint public:public:Rectangle(double x, double y, double w, double h);Rectangle(double x, double y, double w, double h);double double areaarea() const return w () const return w * * h; h; private:private:double w, h;double w, h;静态绑定举例静态绑定举例Rectangle:Rectangle(do
38、uble x, double y, double w, Rectangle:Rectangle(double x, double y, double w, double h) :double h) :PointPoint(x, y), w(w), h(h) (x, y), w(w), h(h) void fun(const void fun(const Point &sPoint &s) ) cout Area = cout Area = s s.area() endl;.area() endl; int main() int main() Rectangle recRecta
39、ngle rec(3.0, 5.2, 15.0, 25.0);(3.0, 5.2, 15.0, 25.0);fun(fun(recrec); ); /类型兼容规则类型兼容规则return 0;return 0; 运行结果:运行结果:Area = 0Area = 0#include#includeusing namespace std;using namespace std;class Point class Point public:public:Point(double x, double y) : x(x), y(y) Point(double x, double y) : x(x), y
40、(y) virtualvirtual double area() const return 0.0; double area() const return 0.0; private:private:double x, y;double x, y;class Rectangle:public Point class Rectangle:public Point public:public:Rectangle(double x, double y, double w, double h);Rectangle(double x, double y, double w, double h);virtu
41、alvirtual double area() const return w double area() const return w * * h; h; private: private:double w, h;double w, h;/其他函数同上例其他函数同上例动态绑定举例动态绑定举例 void fun(const void fun(const Point &sPoint &s) ) cout Area = cout Area = s s.area() endl;.area() endl; int main() int main() Rectangle recRectan
42、gle rec(3.0, 5.2, 15.0, 25.0);(3.0, 5.2, 15.0, 25.0);fun(fun(recrec););return 0;return 0; 运行结果:运行结果:Area = 375Area = 3758.4 虚函数虚函数l虚函数是动态绑定的基础。虚函数是动态绑定的基础。l是非静态的成员函数。是非静态的成员函数。l在类的声明中,在函数原型之前写在类的声明中,在函数原型之前写virtual。l具有继承性,基类中声明了虚函数,派生类中具有继承性,基类中声明了虚函数,派生类中无论是否说明,同原型函数都自动为虚函数。无论是否说明,同原型函数都自动为虚函数。l本质:
43、不是重载声明而是覆盖。本质:不是重载声明而是覆盖。l调用方式:通过基类指针或引用,执行时会调用方式:通过基类指针或引用,执行时会根据指针指向的对象的类,决定调用哪个函数。根据指针指向的对象的类,决定调用哪个函数。#include #include using namespace std;using namespace std;class Base1 /class Base1 /基类基类Base1Base1定义定义public:public:virtual void display() const; ;/虚函数虚函数;void Base1:display() const void Base1:d
44、isplay() const cout Base1:display() endl;cout Base1:display() endl; class Base2: public Base1 /class Base2: public Base1 /公有派生类公有派生类Base2Base2定义定义public:public:voidvoid display() const;() const;/覆盖基类的虚函数覆盖基类的虚函数;void Base2:display() const void Base2:display() const cout Base2:display() endl;cout Bas
45、e2:display() endl; 例例 8-4/公有派生类公有派生类DerivedDerived定义定义class Derived: public Base2 class Derived: public Base2 public:public:void void display() const; /() const; /覆盖基类的虚函数覆盖基类的虚函数;void Derived:display() const void Derived:display() const cout Derived:display() endl;cout Derived:display() display();p
46、tr-display(); /对象指针对象指针-成员名成员名 47int main() int main() /主函数主函数Base1 base1;Base1 base1;/定义定义Base1Base1类对象类对象Base2 base2;Base2 base2;/定义定义Base2Base2类对象类对象Derived derived;Derived derived;/定义定义DerivedDerived类对象类对象fun(&base1);fun(&base1);/用用Base1Base1对象的指针调用对象的指针调用funfun函数函数fun(&base2);fun(&a
47、mp;base2);/用用Base2Base2对象的指针调用对象的指针调用funfun函数函数fun(&derived);fun(&derived);/用用DerivedDerived对象的指针调用对象的指针调用funfun函数函数return 0;return 0; 运行结果:运行结果:Base1:display()Base2:display()Derived:display() 一般虚函数的定义语法如下:一般虚函数的定义语法如下: virtual (形参表形参表) 函数体函数体 其中,被关键字其中,被关键字virtualvirtual说明的函数为虚函数。说明的函数为虚函数。
48、 特别要注意的是,虚函数的声明只能出现在类声明中的特别要注意的是,虚函数的声明只能出现在类声明中的函数原型声明中,而不能出现在成员的函数体实现的时候。函数原型声明中,而不能出现在成员的函数体实现的时候。50设置虚函数须注意以下几点设置虚函数须注意以下几点:l 只有成员函数才能声明为虚函数。因为虚函数只有成员函数才能声明为虚函数。因为虚函数仅适用于有继承关系的类对象,所以普通函数不能仅适用于有继承关系的类对象,所以普通函数不能声明为虚函数。声明为虚函数。l虚函数必须是非静态成员函数。这是因为静态成虚函数必须是非静态成员函数。这是因为静态成员函数不受限于某个对象。员函数不受限于某个对象。内联函数不
49、能声明为虚函数。因为内联函数不能内联函数不能声明为虚函数。因为内联函数不能在运行中动态确定其位置。在运行中动态确定其位置。51l构造函数不能声明为虚函数。构造函数不能声明为虚函数。l 虚函数作为运行过程中多态的基础,主要虚函数作为运行过程中多态的基础,主要是针对对象的,而构造函数是在对象产生之前运是针对对象的,而构造函数是在对象产生之前运行的,因此,虚构造函数是没有意义的。行的,因此,虚构造函数是没有意义的。l析构函数可以声明为虚函数。析构函数可以声明为虚函数。l 析构函数的功能是在该类对象消亡之前进析构函数的功能是在该类对象消亡之前进行一些必要的清理工作。析构函数没有类型,也行一些必要的清理
50、工作。析构函数没有类型,也没有参数,和普通成员函数相比,虚析构函数情没有参数,和普通成员函数相比,虚析构函数情况略为简单些。况略为简单些。虚析构函数虚析构函数为什么需要虚析构函数?为什么需要虚析构函数?l可以通过基类指针删除派生类对象;可以通过基类指针删除派生类对象;l如果你打算允许其他人通过基类指针调用如果你打算允许其他人通过基类指针调用对象的析构函数(通过对象的析构函数(通过delete这样做是正这样做是正常的),就需要让基类的析构函数成为虚常的),就需要让基类的析构函数成为虚函数,否则执行函数,否则执行delete的结果是不确定的。的结果是不确定的。53虚析构函数的声明语法如下:虚析构函
51、数的声明语法如下:Virtual 类名()类名()例如:例如:classBpublic:/virtual B()();54/8_5.cpp#include using namespace std;class Basepublic: virtual Base() cout Base destructorn;class Derived: public Basepublic:Derived();Derived();private:int *i_pointer;Derived:Derived()i_pointer=new int(0); Derived:Derived() cout Derived d
52、estructorn;delete i_pointer;void fun(Base * b) delete b; int main()Base *b=new Derived();fun(b);运行结果运行结果1 1:Base destructorBase destructor运行结果运行结果2 2:Derived destructorDerived destructorBase destructorBase destructor未定义虚函数时未定义虚函数时抽象类抽象类带有纯虚函数的类称为抽象类带有纯虚函数的类称为抽象类:class 类名类名 virtual 类型类型 函数名函数名(参数表参数表
53、)=0; /纯虚函数纯虚函数 .一个抽象类至少带有一个纯虚函数。一个抽象类至少带有一个纯虚函数。8.5 纯虚函数与抽象类纯虚函数与抽象类纯虚函数纯虚函数l纯虚函数是一个在基类中说明的虚函数,纯虚函数是一个在基类中说明的虚函数,它在它在该基类中没有定义具体的操作内容,要求各派该基类中没有定义具体的操作内容,要求各派生类根据实际需要定义自己的实现内容。生类根据实际需要定义自己的实现内容。l纯虚函数的声明形式如下:纯虚函数的声明形式如下: virtual(参数表参数表) =0l纯虚函数与一般虚函数在书写形式上的不同在纯虚函数与一般虚函数在书写形式上的不同在于其后面加了于其后面加了“=0”=0”,表明
54、在基类中不用定义,表明在基类中不用定义该函数,它的实现部分该函数,它的实现部分函数体留给派生类函数体留给派生类去做。去做。 抽象类的主要作用是通过它为一个类族建立抽象类的主要作用是通过它为一个类族建立一个公共的接口,使它们能够更有效地发挥多态一个公共的接口,使它们能够更有效地发挥多态特性。使用抽象类时需注意以下几点。特性。使用抽象类时需注意以下几点。抽象类只能用作其它类的基类,不能建立抽象类抽象类只能用作其它类的基类,不能建立抽象类对象。对象。 抽象类处于继承层次结构的较上层,一个抽抽象类处于继承层次结构的较上层,一个抽象类自身无法实例化,而只能通过继承机制,生象类自身无法实例化,而只能通过继
55、承机制,生成抽象类的非抽象派生类,然后再实例化。成抽象类的非抽象派生类,然后再实例化。抽象类不能用作参数类型、函数返回值或显式抽象类不能用作参数类型、函数返回值或显式转换的类型。转换的类型。可以声明一个抽象类的指针和引用。可以声明一个抽象类的指针和引用。 通过指针或引用,我们就可以指向并访问通过指针或引用,我们就可以指向并访问派生类对象,以访问派生类的成员。派生类对象,以访问派生类的成员。抽象类派生出新的类之后,抽象类派生出新的类之后,如果派生类给出所如果派生类给出所有纯虚函数的函数实现,这个派生类就可以声有纯虚函数的函数实现,这个派生类就可以声明自己的对象,因而不再是抽象类;明自己的对象,因
56、而不再是抽象类;反之,如反之,如果派生类没有给出全部纯虚函数的实现,这时果派生类没有给出全部纯虚函数的实现,这时的派生类仍然是一个抽象类。的派生类仍然是一个抽象类。60例例 8-6#include #include using namespace std;using namespace std;class Base1 /class Base1 /基类基类Base1Base1定义定义public:public:virtual void display() const = 0; /纯虚函数纯虚函数;class Base2: public Base1 /class Base2: public Base1 /公有派生类公有派生类Base2Base2定义定义public:public:void void display() const /() const /覆盖基类的虚函数覆盖基类的虚函数cout Base2:display() endl;cout Base2:display() endl; ;class Derived: public Base2 /class Derived: public Base2 /公有派生类公有派生类DerivedDerived定定义义public:public:void void display() con
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- T/CPIA 0064-2024光伏封装胶膜表面粗糙度测试方法
- T/TIC 004-2023生态环境监测机构综合能力评价通用规范
- T/FSHW 05-2025道路保洁质量与作业规范
- T/CI 1481-2026农民用水合作组织运行制度设计规程
- T/CAAMTB 191-2024车用70MPa加氢口性能试验方法
- T/CPF 0114-2025温室气体 产品碳足迹量化方法与要求 成型纸盒
- 2025~2026学年安徽省阜阳市临泉县校联考北师大版六年级上册11月期中数学试卷【附答案】
- 2025-2026学年陕西省咸阳市初中政治八年级期末下册自测专项攻坚题附答案
- 人工智能在生物多样性监测中的应用
- Photoshop基础技能考核试题(含详细答案)
- 2026年抗菌药物合理使用全员培训计划表
- 戒烟健康科普 拥抱美好人生
- 2025山东沂蒙山天蒙旅游区社会招聘2人笔试历年备考题库附带答案详解
- 华北科技学院《电力电子技术》2025-2026学年第一学期期末试卷(A卷)
- 2025新初任检察官遴选考试核心题库试题及答案
- 软件测试策略全面模板实例
- 浙江强基联盟2026年3月高一联考化学+答案
- 2025年贵州省12月普通高中学业水平合格性考试化学试卷
- 2026年重庆入团测考试题库附答案(精练)
- XXXX公司危险货物运输公司安全风险分级管控管理制度
- 社工专业文书培训
评论
0/150
提交评论