C++多态的实现原理

合集下载

c实现多态的方法

c实现多态的方法

c实现多态的方法
多态是面向对象编程中的一个重要概念,它可以让不同的对象对同一消息做出不同的响应。

在C语言中实现多态一般有以下几种方法: 1. 函数指针:定义一个函数指针类型,不同的类型可以指向不
同的函数实现,通过函数指针调用函数实现多态。

2. 结构体与函数指针组合:定义一个结构体,其中包含函数指
针成员,在不同的结构体中实现不同的函数,通过结构体指针调用不同的函数实现多态。

3. 函数指针数组:定义一个函数指针数组,数组中不同的元素
可以指向不同的函数实现,通过数组索引调用不同的函数实现多态。

需要注意的是,在C语言中实现多态需要手动管理内存,因此需要谨慎使用,避免内存泄漏等问题。

- 1 -。

C语言中的多态

C语言中的多态

C语⾔中的多态⼀、多态的主要特点1、继承体系下。

继承:是⾯向对象最显著的⼀个特性。

继承是从已有的类中派⽣出新的类,新的类能吸收已有类的数据属性和⾏为,并能扩展新的能⼒,已有类被称为⽗类/基类,新增加的类被称作⼦类/派⽣类。

2、⼦类对⽗类的虚函数进⾏重写。

3、虚表。

在⾯向对象语⾔中,接⼝的多种不同现⽅式即为多态。

同⼀操作作⽤于不同的对象,可以有不同的解释,产⽣不同的执⾏结果,这就是多态性。

简单说就是允许基类的指针指向⼦类的对象。

⼆、代码实现1、C++中的继承与多态1 class Base2 {3 public:4 virtual void fun() {} //基类函数声明为虚函数5 int B1;6 };7 class Derived :public Base //Derived类公有继承Base类8 {9 public:10 virtual void fun() { //函数重写,此时基类函数可以声明为虚函数,也可以不声明11 cout << "D1.fun" << endl;12 }13 int D1;14 };15 int main(){16 Base b1; //创建⽗类对象17 Derived d1;//创建⼦类对象1819 Base *p1 = (Base *)&d1;//定义⼀个⽗类指针,并通过⽗类指针访问⼦类成员20 p1->fun();2122 Derived *p2 = dynamic_cast<Derived*> (&b1); //dynamic_cast⽤于将⼀个⽗类对象的指针转换为⼦类对象的指针或引⽤(动态转换)23 p2->fun();2425 getchar();26 return 0;27 }2. C语⾔实现C++的继承与多态1 typedef void(*FUNC)(); //定义⼀个函数指针来实现对成员函数的继承2 struct _Base //⽗类3 {4 FUNC _fun;//由于C语⾔中结构体不能包含函数,故借⽤函数指针在外⾯实现5 int _B1;6 };7 struct _Derived//⼦类8 {9 _Base _b1;//在⼦类中定义⼀个基类的对象即可实现对⽗类的继承10 int _D1;11 };12 void fb_() //⽗类的同名函数13 {14 printf("_b1:_fun()\n");15 }16 void fd_() //⼦类的同名函数17 {18 printf("_d1:_fun()\n");19 }20 int main() {21 _Base _b1;//定义⼀个⽗类对象_b122 _Derived _d1;定义⼀个⼦类对象_d12324 _b1._fun = fb_;//⽗类的对象调⽤⽗类的同名函数25 _d1._b1._fun = fd_;//⼦类的对象调⽤⼦类的同名函数2627 _Base *_p1 = &_b1;//定义⼀个⽗类指针指向⽗类的对象28 _p1-> _fun(); //调⽤⽗类的同名函数2930 _p1 = (_Base *)&_d1;//让⽗类指针指向⼦类的对象,由于类型不匹配所以要进⾏强转31 _p1->_fun(); //调⽤⼦类的同名函数3233 getchar();34 return 0;35 }。

多态的概念和作用(深入理解)

多态的概念和作用(深入理解)

多态的概念和作⽤(深⼊理解)多态是⾯向对象的重要特性,简单点说:“⼀个接⼝,多种实现”,就是同⼀种事物表现出的多种形态。

编程其实就是⼀个将具体世界进⾏抽象化的过程,多态就是抽象化的⼀种体现,把⼀系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进⾏对话。

对不同类的对象发出相同的消息将会有不同的⾏为。

⽐如,你的⽼板让所有员⼯在九点钟开始⼯作, 他只要在九点钟的时候说:“开始⼯作”即可,⽽不需要对销售⼈员说:“开始销售⼯作”,对技术⼈员说:“开始技术⼯作”, 因为“员⼯”是⼀个抽象的事物, 只要是员⼯就可以开始⼯作,他知道这⼀点就⾏了。

⾄于每个员⼯,当然会各司其职,做各⾃的⼯作。

多态允许将⼦类的对象当作⽗类的对象使⽤,某⽗类型的引⽤指向其⼦类型的对象,调⽤的⽅法是该⼦类型的⽅法。

这⾥引⽤和调⽤⽅法的代码编译前就已经决定了,⽽引⽤所指向的对象可以在运⾏期间动态绑定。

再举个⽐较形象的例⼦:⽐如有⼀个函数是叫某个⼈来吃饭,函数要求传递的参数是⼈的对象,可是来了⼀个美国⼈,你看到的可能是⽤⼑和叉⼦在吃饭,⽽来了⼀个中国⼈你看到的可能是⽤筷⼦在吃饭,这就体现出了同样是⼀个⽅法,可以却产⽣了不同的形态,这就是多态!多态的作⽤:1. 应⽤程序不必为每⼀个派⽣类编写功能调⽤,只需要对抽象基类进⾏处理即可。

⼤⼤提⾼程序的可复⽤性。

//继承2. 派⽣类的功能可以被基类的⽅法或引⽤变量所调⽤,这叫向后兼容,可以提⾼可扩充性和可维护性。

//多态的真正作⽤,以前需要⽤switch实现----------------------------------------------------多态是⾯向对象程序设计和⾯向过程程序设计的主要区别之⼀,何谓多态?记得在CSDN⾥⼀篇论C++多态的⽂章⾥有⼀名话:“龙⽣九⼦,⼦⼦不同”多态就是同⼀个处理⼿段可以⽤来处理多种不同的情况,在钱能⽼师的《C++程序设计教程》书中有这样⼀个例⼦:定义了⼀个⼩学⽣类[本⽂全部代码均⽤伪码]class Student{public:Student(){}~Student(){}void 交学费(){}//......};⾥⾯有⼀个 “交学费”的处理函数,因为⼤学⽣和⼩学⽣⼀些情况类似,我们从⼩学⽣类中派⽣出⼤学⽣类:class AcadStudent:public Student{public:AcadStudent(){}~ AcadStudent(){}void 交学费(){}//.......};我们知道,中学⽣交费和⼤学⽣交费情况是不同的,所以虽然我们在⼤学⽣中继承了中学⽣的"交学费"操作,但我们不⽤,把它重载,定义⼤学⽣⾃⼰的交学费操作,这样当我们定义了⼀个⼩学⽣,⼀个⼤学⽣后:Student A;AcadStudent B;A.交学费(); 即调⽤⼩学⽣的,B.交学费();是调⽤⼤学⽣的,功能是实现了,但是你要意识到,可能情况不仅这两种,可能N种如:⼩学⽣、初中⽣、⾼中⽣、研究⽣.....它们都可以以Student[⼩学⽣类]为基类。

C--程序设计--第10章-多态性及虚函数

C--程序设计--第10章-多态性及虚函数

使用重载函数注意:
不要使用重载函数描述不相干的函数 在类中,构造函数和普通成员函数均可以
重载 避免与函数的默认参数产生二义性
二、运算符重载
运算符重载(operate overloading)就是 赋予已有的运算符多重含义。
运算符重载实质是函数重载,运算符重载 的选择与函数重载类似,会根据运算符的 操作数的类型、个数和顺序来进行运算符 函数的选择。
#include<iostream.h> str#iinngc:l:usdter<isntgr(icnhga.rh>*s) v{}ossccsssc{s{{ittohtttolsstlsssls*drruarrrueptrepttepsi1trii3tc{pn=rin=rrn=pmn.<nn.<lprgncngncign=agp<*ggp<auitepgtepnte'irssrssbv\hwy:hwyghwnsit1ssitsla0=(:=(:=(tnr=ttnrit'scssscs:sc)rt1"rrt3scesss~ivci;thpt1hpsih1(.T23(.t::tttsnohn}ra,r.a,tza()gh(()grrrrttiatlrsilrsrer";eass;eiiiirdre[)ne[1i;[Ttt1ttnnnniglnl;gnl.nlhl)rlggggnep*e(e}(gesgeiei;2e(((gtrsnsnstnp(nsns)ncsi(lipg)gthg)ig(;(htn)en;t;tr;t;nti)a)artnthhih}ths<<ri{((;+n++<p<snd))}1g1s1aere*ige;]]i]nonszl{{;&;z;ddgd)&eercseelrl;s=teo1)m;a;/18etu)om/)0ut..;)构sr<""/;pn<造);//;s;/复}lp函构e<制n<数造ge构tn函hd造;l数};重} 载

C++中的封装、继承、多态理解

C++中的封装、继承、多态理解

C++中的封装、继承、多态理解封装(encapsulation):就是将抽象得到的数据和⾏为(或功能)相结合,形成⼀个有机的整体,也就是将数据与操作数据的源代码进⾏有机的结合,形成”类”,其中数据和函数都是类的成员。

封装的⽬的是增强安全性和简化编程,使⽤者不必了解具体的实现细节,⽽只是要通过外部接⼝,特定的访问权限来使⽤类的成员。

封装可以隐藏实现细节,使得代码模块化。

继承(inheritance):C++通过类派⽣机制来⽀持继承。

被继承的类型称为基类或超类,新产⽣的类为派⽣类或⼦类。

保持已有类的特性⽽构造新类的过程称为继承。

在已有类的基础上新增⾃⼰的特性⽽产⽣新类的过程称为派⽣。

继承和派⽣的⽬的是保持已有类的特性并构造新类。

继承的⽬的:实现代码重⽤。

派⽣的⽬的:实现代码扩充。

三种继承⽅式:public、protected、private。

继承时的构造函数:(1)、基类的构造函数不能被继承,派⽣类中需要声明⾃⼰的构造函数;(2)、声明构造函数时,只需要对本类中新增成员进⾏初始化,对继承来的基类成员的初始化,⾃动调⽤基类构造函数完成;(3)、派⽣类的构造函数需要给基类的构造函数传递参数;(4)、单⼀继承时的构造函数:派⽣类名::派⽣类名(基类所需的形参,本类成员所需的形参):基类名(参数表) {本类成员初始化赋值语句;};(5)、当基类中声明有默认形式的构造函数或未声明构造函数时,派⽣类构造函数可以不向基类构造函数传递参数;(6)、若基类中未声明构造函数,派⽣类中也可以不声明,全采⽤缺省形式构造函数;(7)、当基类声明有带形参的构造函数时,派⽣类也应声明带形参的构造函数,并将参数传递给基类构造函数;(8)、构造函数的调⽤次序:A、调⽤基类构造函数,调⽤顺序按照它们被继承时声明的顺序(从左向右);B、调⽤成员对象的构造函数,调⽤顺序按照它们在类中的声明的顺序;C、派⽣类的构造函数体中的内容。

继承时的析构函数:(1)、析构函数也不被继承,派⽣类⾃⾏声明;(2)、声明⽅法与⼀般(⽆继承关系时)类的析构函数相同;(3)、不需要显⽰地调⽤基类的析构函数,系统会⾃动隐式调⽤;(4)、析构函数的调⽤次序与构造函数相反。

C的运行时类型识别实现动态多态性

C的运行时类型识别实现动态多态性

C的运行时类型识别实现动态多态性在C语言中,没有原生的运行时类型识别(Runtime Type Identification,RTTI)机制,而RTTI是实现多态性的关键。

然而,我们可以通过一些技巧和约定来模拟实现动态多态性,即在运行时根据对象的类型来决定调用哪个函数。

本文将介绍一种常用的C语言中实现动态多态性的方法。

一、使用函数指针表进行类型识别为了实现运行时类型识别,我们可以使用函数指针表(Function Pointer Table)来存储不同类型对象的函数指针。

函数指针表是一个包含一组函数指针的数组,数组的索引对应于对象的类型。

首先,我们定义一个基础的类型,作为其他类型的父类型,例如Shape类型:```ctypedef struct {void (*draw)();} Shape;```接下来,我们定义继承自Shape的具体类型,例如Rectangle和Circle:```ctypedef struct {Shape shape;int width;int height;} Rectangle;typedef struct {Shape shape;int radius;} Circle;```我们为每个具体类型实现相应的draw函数:```cvoid rectangle_draw() {printf("Drawing rectangle\n");}void circle_draw() {printf("Drawing circle\n");}```然后,我们为每个具体类型创建函数指针表,并将draw函数指针赋值给相应的表项:```cShape shape;shape.draw = rectangle_draw;Shape shape;shape.draw = circle_draw;```现在,我们可以通过调用shape.draw()来动态地调用相应类型的draw函数。

c++三大特征的理解

c++三大特征的理解

c++三大特征的理解
C++语言的三大特征是封装、继承和多态。

封装是指将数据和操作数据的方法捆绑在一起,以防止外部访
问和不合法修改,从而保证数据的安全性和一致性。

通过封装,可
以隐藏实现细节,使得对象更容易被使用,并且减少了对外部的依赖。

继承是指一个类可以派生出新的类,新的类可以继承原有类的
属性和方法,并且可以添加新的属性和方法。

继承可以提高代码的
复用性,减少重复编码,同时也能够实现多态性。

多态性是指同一个消息被不同的对象接收时,可以产生不同的
行为。

在C++中,多态性可以通过虚函数和纯虚函数来实现。

多态
性使得程序更加灵活,能够根据不同对象的类型来执行不同的操作,从而提高了代码的可扩展性和可维护性。

总的来说,封装、继承和多态是C++语言的三大特征,它们为
面向对象编程提供了强大的支持,使得程序更加模块化、灵活和易
于扩展。

c++底层实现原理

c++底层实现原理

c++底层实现原理C++底层实现原理C++是一种多范式编程语言,也就是说,它可以用很多种不同的方式来实现同一个程序。

正如它的另外一个名字“多态编程语言”那样,它可以在编译时不同地表达相同的程序。

例如,C++可以以多种方式表达函数,可以使用内联函数,也可以使用汇编或者预处理宏。

因为这种多态的特性,C++在底层实现原理上也有一些特点。

1.可重用模版C++的可重用模版的实现原理是,一个模版的编译过程中,编译器会根据传给模版的参数创建对应的不同实例。

这些实例有自己的数据类型和定义,这就叫做类模版实例化。

在编译期间,编译器会根据模版的定义,在每一个模版实例的地方,产生模版实例的代码。

这也是为什么使用模版可以更加容易的实现代码重用的原因。

2.内联函数内联函数的原理是,编译器会把定义的函数在调用之前展开,这样就可以避免调用函数时产生的开销。

这样的开销可以包括压栈、函数指针寻址、返回值等。

编译器展开函数的时候,根据它们的调用参数,会执行函数体,它会把函数体的每一行复制到函数调用处,然后把函数调用替换成函数体中每一行的执行结果。

这样就可以大大提高程序的执行效率。

3.汇编汇编语言是一种机器级语言,可以直接控制CPU的功能。

汇编语言可以使用汇编代码来定义函数,也可以用汇编代码来定义寄存器的使用,包括加减法、逻辑移位等功能。

汇编语言可以让程序员更容易的掌握底层的硬件操作,以及节省程序的运行时间。

4.预处理宏预处理宏是一种可以在源程序编译之前,执行一些指令的一种宏语言。

它可以在编译时将常量、变量或表达式替换成确定的值,这样就可以有效的提高程序的执行效率。

预处理宏可以被用来实现宏定义、宏引用及复杂的宏功能。

总结C++是一种多范式编程语言,它的底层实现原理也体现一种多态,包括模版、内联函数、汇编和预处理宏等,它们都可以帮助提高程序的执行效率,提高代码重用性以及实现高效的底层硬件操作。

C++(面向对象的程序设计)考前必背的名词解释和简答题

C++(面向对象的程序设计)考前必背的名词解释和简答题

C++(面向对象的程序设计)考前必背的名词解释和简答题1. 封装封装是将数据和代码捆绑到一起,避免了外界的干扰和不确定性。

例如C++中的类,它定义了该集合中每个对象所共有的属性和方法。

2. 继承继承是让某个类型的对象获得另一个类型的对象的特征。

例如:C++中子类对父类的继承,子类具有父类的特性,同时还可以拥有自己的新特性。

3. 多态多态是指不同类型的对象接收相同的消息时产生不同的行为。

多态机制使具有不同内部结构的对象可以共享相同的外部接口,通过这种方式减小代码的复杂度。

例如函数的重载。

4. 什么是this 指针?为什么要用this 指针?this 指针是类中的一个特殊指针,当类实例化时,this 指针指向对象自己;而在类的声明时,指向类本身。

通过它不仅可以提升成员函数操作的效率,而且能简化运算符重载代码。

5. 叙述公有、私有、保护成员在类中、类外和对象中的访问权限。

类中的关键字public, private, protected 声明了类中的成员与类外之间的关系,称为访问权限。

对于public 成员来说,他们是公有的,可以在类外和对象中访问。

对于private 成员来说,他们是私有的,不能在类外和对象中访问,数据成员只能由类中的函数使用,成员函数只允许在类中调用。

对于protected 成员来说,他们是受保护的,具有半公开性质,可以在类中与子类中访问。

6. 构造函数和析构函数的作用是什么?构造函数的功能是在创建对象时,给数据成员赋初值,即给对象初始化。

析构函数的功能是释放一个对象,在对象删除前,用来做一些内存释放等清理工作。

7. 什么是类的继承和派生?继承是指一个事物可以继承其父辈全部或部分的特性,同时本身还有自己的特性。

当一个新类从一个已定义的类中派生后,新类不仅继承了原有类的属性和方法,并且还拥有自己新的属性和方法,称为类的继承和派生。

8. 派生类public 继承方式有那些特点?(1)在派生类中,基类的公有成员、保护成员和私有成员的访问属性保持不变。

C 实验多态性实验报告

C   实验多态性实验报告
3、 代码如下: #include<iostream.h>
class Point { public:
Point(int xx,int yy):x(xx),y(yy) {} void display()const; Point &operator++(); Point operator++(int); Point &operator--(); Point operator--(int); private:
using namespace std;

int Double(int x);
long Double(long x);
float Double(float x);
double Double(double x);
int main()
{ int myInt = 6500;
cout<<Double(myInt)<<endl;
学习使用虚函数实现动态多态性。而虚函数就是在基类中被关键字 virtual 说明,
实 并在派生类中重新定义的函数,且在派生类中重工业新定义时,函数原型,包括返回
类型、函数名、参数个数与参数类型的顺序,都必须与基类中的完全相同。此外,构 验
造函数不能是虚函数,但析构函数可以是虚函数。

函数的重载方法有一参数个数相同,但是类型不同;二参数个数不同;三 coust

验 Visual C++的编译环境下,独立完成实验要求的内容,独立完成编写、编译以及运行

的过程


验 安装了 Visual C++的 PC 机器

C++知识点总结

C++知识点总结

类和对象初步1.类的定义在定义外成员函数的实现2.类的成员函数之间可以相互调用,类的成员函数也可以重载,也可设默认参数值3.一般来讲,一个对象占用的内存空间的大小等于其成员变量的体积之和。

每个对象都有自己的存储空间(成员变量),但成员函数只有一份对象名.成员名指针->成员名引用名.成员名4.private:一个类的私有成员,只能在该类的成员函数中才能访问public:proteced:5.class默认private struct默认public6.内联成员函数:成员函数名前加inline 或函数体写在类定义内部的成员函数。

执行更快,但会带来额外的内存开销构造函数1.构造函数全局变量在堆上,系统自动初始化为零。

局部变量在栈上,初始值是随机的,需要初始化。

2.构造函数:对对象进行初始化。

构造函数执行时对象的内存空间已经分配,构造函数的作用是初始化这片空间.可重载,不写的话有默认构造函数,但是如果编写了构造函数,那默认构造函数不会再执行.是一类特殊的成员函数。

不写返回值类型,函数名为类名.3.对象在生成时一定会调用某个构造函数,一旦生成,不再执行构造函数.4.P183 Ctest *pArray[3]={new Ctest(4),new Ctest(1,2)}5.复制构造函数:其是构造函数的一种,只有一个参数,为本类的引用,防止混淆,构造函数不能以本类的对象作为唯一的参数。

默认复制构造函数。

6.复制构造函数被调用的三种情形:1用一个对象去初始化另一个对象时Complex C1(C2)ComplexC2=C1; 2 函数的参数是类A的对象。

形参未必等于实参函数中用对象的引用不会调用复制构造函数void Function(const Complex &c)3 函数的返回值是类A的对象7.类型转换构造函数:除复制构造函数外,只有一个参数的构造函数C=68.析构函数:在对象消亡时调用,可以定义其做善后工作。

多态性C++

多态性C++

5.2 函数重载
编译时多态性可以通过函数重载来实现.函数重载的意义在于它能用同一个名 字访问一组相关的函数,也就是说,能使用户为某一类操作取一个通用的名字, 而由编译程序来选择具体由哪个函数来执行,因而有助于解决程序的复杂性问题. 普通成员函数和构造函数都可以重载 例5.1 基类和派生类中函数重载的例子 Void main() { point p(20,20); #include <iostream.h> circle c(8,8,30); class point{ cout<<p.area()<<endl; //执行基类point中的area()的函数 int x,y; cout<<c.area()<<endl; public: //执行派生类circle中的area()函数 cout<<c.point::area()<<endl; point(int a,int b){x=a;y=b;} //执行基类point中的area()的函数 float area(){return 0.0;} } 程序运行结果为: }; //基类中的area函数 0 class circle:public point{ 2827.439941 0 int radius; public: circle(int x,int y,int rad):point(x,y){radius=rad;} float area(){return 3
//构造函数 //重载复数"+"运算符 //重载复数"-"运算符 //重载复数"*"运算符 //重载复数"/"运算符 //定义构造函数
接1 例5.3 complex complex::operator +(complex c) { complex temp; temp.real=real+c.real; temp.imag=imag+c.imag; return temp; } complex complex::operator -(complex c) { complex temp; temp.real=real-c.real; temp.imag=imag-c.imag; return temp; } //重载"+"定义

【转】什么是多态,怎样实现多态

【转】什么是多态,怎样实现多态

【转】什么是多态,怎样实现多态C++中多态是怎样实现的?多态是⼀种不同的对象以单独的⽅式作⽤于相同消息的能⼒,这个概念是从⾃然语⾔中引进的。

例如,动词“关闭”应⽤到不同的事务上其意思是不同的。

关门,关闭银⾏账号或关闭⼀个程序的窗⼝都是不同的⾏为;其实际的意义取决于该动作所作⽤的对象。

⼤多数⾯向对象语⾔的多态特性都仅以虚拟函数的形式来实现,但C++除了⼀般的虚拟函数形式之外,还多了两种静态的(即编译时的)多态机制:1、操作符重载:例如,对整型和串对象应⽤ += 操作符时,每个对象都是以单独的⽅式各⾃进⾏解释。

显然,潜在的 += 实现在每种类型中是不同的。

但是从直观上看,我们可以预期结果是什么。

2、模板:例如,当接受到相同的消息时,整型vector对象和串vector对象对消息反映是不同的,我们以关闭⾏为为例:vector < int > vi; vector < string > names;string name("VC知识库");vi.push_back( 5 ); // 在 vector 尾部添加整型names.push_back (name); // 添加串和添加整型体现差别的潜在的操作静态的多态机制不会导致与虚拟函数相关的运⾏时开。

此外,操作符重载和模板两者是通⽤算法最基本的东西,在STL中体现得尤为突出。

那么接下来我们说说以虚函数形式多态:通常都有以重载、覆盖、隐藏来三中⽅式,三种⽅式的区别⼤家应该要很深⼊的了解,这⾥就不多说了。

许多开发⼈员往往将这种情况和C++的多态性搞混淆,下⾯我从两⽅⾯为⼤家解说:1、编译的⾓度C++编译器在编译的时候,要确定每个对象调⽤的函数的地址,这称为早期绑定(early binding)。

2、内存模型的⾓度为了确定对象调⽤的函数的地址,就要使⽤迟绑定(late binding)技术。

当编译器使⽤迟绑定时,就会在运⾏时再去确定对象的类型以及正确的调⽤函数。

多态常见的经典面试题

多态常见的经典面试题

1、请谈谈你对多态的理解?谈多态的现象
2、谈谈C++编译器是如何实现多态?谈C++编译器实现原理
3、重写PK 重载理解谈多态的现象
4、是否可以将类的每个成员函数都声明为虚函数,为什么。

谈多态的C++编译器实现原理
说明1:
通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。

而普通成员函数是在编译时就确定了调用的函数。

在效率上,虚函数的效率要低很多。

说明2:
出于效率考虑,没有必要将所有成员函数都声明为虚函数
5、构造函数中调用虚函数能实现多态吗?为什么?谈多态的C++编译器实现原理
能调用但是不是多态,具体见视频
6、虚函数表指针(vptr)被编译器初始化的过程,你是如何理解的?同上
7、构造函数能定义成虚函数吗?谈多态的现象
8、为什么要定义虚析构函数?虚析构函数的应用场景?谈多态的应用
需要通过多态(积累指针去)释放子类对象资源
9、铁律1:指针也是一种数据类型,在C++中的引申
积累和子类对象指针++混搭风
被实际开发经验抛弃的多继承
工程开发中真正意义上的多继承几乎是不被使用的
多重继承所带来的代码复杂性远高于其所带来的便利性
多重继承对于代码维护性上的影响是灾难性的
在设计方法上,任何多继承都可以用单继承代替
绝大多数面向对象语言都不支持多继承
绝大多数面向对象语言都支持接口的概念
C++中没有接口的概念
C++中可以使用纯虚函数实现接口
接口类中只有函数原型定义,没有任何数据的定义。

c语言中多态的定义及实现方式

c语言中多态的定义及实现方式

c语言中多态的定义及实现方式C语言是一种面向过程的编程语言,不支持面向对象编程的特性,如多态、继承和封装等。

但是,我们可以通过一些技巧来实现类似于面向对象编程中的多态性。

在本文中,我们将介绍C语言中多态的定义、实现方式以及举出一些例子。

1.多态的定义多态是面向对象编程中的一个重要概念。

它指的是不同对象对同一消息作出不同响应的能力。

在C语言中,我们可以通过函数指针、结构体和联合体等技术来实现多态性。

下面是多态的定义:多态是指在不同的对象上调用同一方法,而这些对象会根据所属类的不同产生不同的行为。

换句话说,多态是指一个接口,多种实现。

1.多态的实现方式在C语言中,我们可以通过以下方式来实现多态性:2.1 函数指针函数指针是指向函数的指针变量。

我们可以将不同的函数指针赋值给同一个函数指针变量,从而实现多态性。

例如:#include <stdio.h>void add(int a,int b){printf("%d + %d = %d\n", a, b, a + b);}void sub(int a,int b){printf("%d - %d = %d\n", a, b, a - b);}int main(){void(*p)(int,int);int a =10, b =5;p = add;p(a, b);p = sub;p(a, b);return0;}在上面的例子中,我们定义了两个函数add和sub,它们实现了两种不同的行为。

我们定义了一个函数指针p,它可以指向这两个函数。

在不同的情况下,我们将p 指向不同的函数,从而实现了多态性。

2.2 结构体结构体是一种自定义的数据类型,它可以包含多个不同类型的成员。

我们可以通过结构体来实现多态性。

例如:#include <stdio.h>typedef struct Animal{void(*speak)();} Animal;typedef struct Cat{Animal base;} Cat;typedef struct Dog{Animal base;} Dog;void cat_speak(){printf("Meow!\n");}void dog_speak(){printf("Woof!\n");}int main(){Cat cat;Dog dog;cat.base.speak = cat_speak;dog.base.speak = dog_speak;cat.base.speak();dog.base.speak();return0;}在上面的例子中,我们定义了一个Animal结构体和两个派生结构体Cat和Dog。

1在C中多态性体现在哪几方面?

1在C中多态性体现在哪几方面?

1 在C++ 中多态性体现在哪几方面?答案:在C++中,多态性体现在两个方面:编译多态性——对同一消息的不同操作是在程序编译时就确定了,即静态多态性。

运行多态性——对同一消息的不同操作是在程序运行时根据不同的运行状况才确定,即动态多态性。

2 函数重载与虚函数有哪些相同点与不同点?答案:函数重载与虚函数之间的相同点是多个函数版本具有相同的函数名,即表现出对同一消息的不同操作。

而二者之间的不同点表现在:⑴函数重载的不同函数版本既允许存在于同一类中,也允许基类的成员函数在派生类中重载。

存在于同一类中的不同重载函数版本的参数(类型、个数、顺序)必须有所不同。

如果存在于基类和派生类中的成员函数的原型完全一致,则派生类的成员函数将覆盖基类的同名函数。

⑵虚函数是用来表现基类和公有派生类的相同原型成员函数之间的关联关系的实现机制,因此这种同原型成员函数必须分属于基类和派生类,并且首先在基类中用关键字virtual 声明虚函数;一个虚函数一旦被定义,就可以在该基类的一个或多个直接或间接派生类中被重新定义;虚函数重新定义时,其函数原型,即包括返回类型、函数名、参数的类型、个数和顺序,都必须与基类中的原型完全一致。

3 虚函数是如何实现面向对象系统的多态性的,它会带来什么益处?答案:在一个具有公有派生关系的类层次结构中,只要在基类中将某个接口函数声明为虚函数,并在该基类的直接和间接公有派生类中重新定义该虚函数的不同新版本,就可以实现在程序运行期间,使用一个基类指针动态地指向基类和从该基类直接或间接派生的任何类的对象,并通过该指针调用虚函数在不同类中定义的不同版本,即动态多态性。

显然,虚函数为面向对象系统提供了一种更为灵活的多态性,这种多态能力对于期望在基类中为从该基类派生的所有类定义统一的操作接口的设计尤为重要。

4 下面有两段程序,判断它们是否正确,若有错误,给予纠正。

①class base{// …public:virtual void show();};class derive : public base{// …public:void show();};main(){base obj1, obj2, *ptr1;derive obj3, obj4, *ptr2;ptr1 = &obj1;ptr1->show();ptr1 = &obj3;ptr1->show();ptr2 = &obj4;ptr2->show();ptr2 = &obj2;ptr2->show();// …}②class fruit{// …public:virtual void show() = 0;// …};class apple : public fruit{// …public:void show();// …};main(){fruit fru, *ptr;apple app;ptr = &app;ptr->show();ptr = &fru;ptr->show();// …}答案:①该程序的main()中出现了使用派生类指针指向基类对象,并通过该指针调用基类虚函数的错误语句(ptr2 = &obj2; ptr2->show();),因此,应该将main()修改为:main(){base obj1, obj2, *ptr1;derive obj3, obj4, *ptr2;ptr1 = &obj1;ptr1->show();ptr1 = &obj3;ptr1->show();ptr2 = &obj4;或ptr1 = &obj4;ptr2->show(); 或ptr1->show();// …}②该程序的main()中出现了为抽象类fruit创建对象,并通过基类指针调用纯虚函数的错误语句(fruit fru, ptr = &fru; ptr->show();),因此,应该将main()修改为:main(){fruit *ptr;apple app;ptr = &app;ptr->show();// …}5 判断下列各段程序是否可以通过编译?为什么?⑴#include <iomanip.h>class X{public:X() { }virtual void foo();};class Y : public X{public:Y() { }void foo() { cout << “Y’s foo invoked” << endl; }};int main() { return 0; }答案:由于基类中的虚函数没有定义实现代码,所以不能通过编译。

C#实现多态(简单例子)

C#实现多态(简单例子)

C#实现多态(简单例子)继承是子类使用了父类的方法,多态是父类使用了子类的方法。

A.多态性定义:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。

多态性通过派生类重载基类中的虚函数型方法来实现。

C#支持两种类型的多态性:1.编译时的多态性编译时的多态性是通过重载来实现的。

对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。

2.运行时的多态性运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。

C#中,运行时的多态性通过虚成员实现。

编译时的多态性为我们提供了运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点。

B.实现多态(多态性允许对类的某个方法(同名方法)进行调用而无需考虑该方法所提供的特定实现。

)1.抽象类实现2.继承实现3.接口实现using System;using System.Collections.Generic;using System.Text;namespace Video{class Program{static void Main(string[] args){Program p = new Program();p.PlayVideo();}public void PlayVideo(){VideoShow vw;vw = new DVD();Console.WriteLine(vw.playVideo());vw = new VCD();Console.WriteLine(vw.playVideo());}}public abstract class VideoShow //不能被实例化{public abstract string playVideo();}public class VCD:VideoShow{public override string playVideo(){return "正在播放VCD";}}public class DVD : VideoShow{public override string playVideo(){return "正在播放DVD";}}}using System;using System.Collections.Generic;using System.Text;namespace DoorApplication{class Program{static void Main(string[] args){IDoor[] door = new IDoor[100];//定义一个接口数组for(int i=0;i<100;i++){if (i % 2==0)door[i]=new door1();elsedoor[i]=new door2();door[i].Close();//接口实现方法的访问door[i].Open();}}}interface IDoorvoid Open();void Close();}class door1:IDoor{private bool HavePerson=false;#region IDoor 成员public void Open(){if(HavePerson)Console.WriteLine("door1门打开了"); }public void Close(){if(!HavePerson)Console.WriteLine("door1门关上了"); }#endregion}class door2:IDoor{private bool isPassWord = false;#region IDoor 成员public void Open(){if (isPassWord)Console.WriteLine("door2门打开了"); }public void Close()if (!isPassWord)Console.WriteLine("door2门关上了");}#endregion}}继承多态性:继承多态性是最常见的形式。

C#多态实例详解

C#多态实例详解

----C#多态实例详解----多态是面向对象编程中三大机制之一,其原理建立在"从父类继承而来的子类可以转换为其父类"这个规则之上,换句话说,能用父类的地方,就能用该类的子类.当从父类派生了很多子类时,由于每个子类都有其不同的代码实现,所以当用父类来引用这些子类时,同样的操作而可以表现出不同的操作结果,这就是所谓的多态.1.了解什么是多态性2.如何定义一个虚方法3.如何重载一个虚方法4.如何在程序中运用多态性面向对象程序设计中的另外一个重要概念是多态性。

在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。

可以把一组对象放到一个数组中,然后调用它们的方法,在这种场合下,多态性作用就体现出来了,这些对象不必是相同类型的对象。

当然,如果它们都继承自某个类,你可以把这些派生类,都放到一个数组中。

如果这些对象都有同名方法,就可以调用每个对象的同名方法。

本节课将向你介绍如何完成这些事情。

1.清单9-1. 带有虚方法的基类:DrawingObject.csusing System;public class DrawingObject{public virtual void Draw(){Console.WriteLine("I'm just a generic drawing object.");}}说明清单9-1 定义了DrawingObject类。

这是个可以让其他对象继承的基类。

该类有一个名为Draw()的方法。

Draw()方法带有一个virtual修饰符,该修饰符表明:该基类的派生类可以重载该方法。

DrawingObject类的Draw()方法完成如下事情:输出语句"I'm just a generic drawing object."到控制台。

2.清单9-2. 带有重载方法的派生类:Line.cs, Circle.cs, and Square.csusing System;public class Line : DrawingObject{public override void Draw(){Console.WriteLine("I'm a Line.");}}public class Circle : DrawingObject{public override void Draw(){Console.WriteLine("I'm a Circle.");}}public class Square : DrawingObject{public override void Draw(){Console.WriteLine("I'm a Square.");}}说明清单9-2定义了三个类。

多态的实现原理

多态的实现原理

多态的实现原理多态是面向对象编程语言的一种重要特性,其可以使得程序在运行时动态地选择调用哪个具体方法,从而增加代码的灵活性和可维护性。

在Java等编程语言中,多态主要是通过接口和继承实现的。

本文将介绍多态的实现原理及其基本概念。

1. 多态的基本概念多态是指同一对象在不同情形下的多种表现形态。

更具体地说,多态有两种形式:编译时多态(静态多态)和运行时多态(动态多态)。

编译时多态是指在编译阶段就能确定所调用的方法,也称为静态多态。

其主要实现方式是方法重载,即在同一个类中定义多个同名的方法,但它们的方法参数类型、数量或顺序不同。

编译器会根据传入参数的不同自动选择具体的方法。

运行时多态是指程序在运行阶段才能根据具体情况动态地选择调用哪个方法,也称为动态多态。

其主要实现方式是方法覆盖,即在子类中重新定义与父类中相同的方法名称和参数列表。

这样,在运行时,调用子类对象的方法时,编译器会首先在子类中查找该方法,如果找到,则直接调用子类中的方法;如果没有找到,则会去父类中查找该方法。

这种机制也称为“虚方法调用”。

2. 多态的实现方式在Java等编程语言中,多态主要是通过继承和接口实现的。

继承是指一个类从另一个类继承属性和方法,并且可以重写方法。

接口是指一组方法声明,而没有方法体,子类可以实现这些方法。

在下面的例子中,我们借助Java语言来说明实现多态的两种方式。

首先,我们定义一个抽象类和一个实现这个抽象类的子类:javaabstract class Animal {public abstract void say();}class Cat extends Animal {public void say() {System.out.println("I'm a cat.");}}抽象类Animal定义了一个抽象方法say(),而Cat类继承了Animal类,并重写了say()方法。

接下来,我们创建一个方法,该方法接收一个Animal类型的参数,并调用该参数的say()方法:javapublic static void makeSound(Animal animal) {animal.say();}在调用makeSound方法时,我们可以传递一个Animal类型的对象或一个Cat 类型的对象,代码如下:javapublic static void main(String[] args) {Animal animal = new Cat();makeSound(animal); 输出:I'm a cat.}因为Cat类继承了Animal类并重写了say()方法,在调用makeSound方法时,我们将Cat类型的对象传递给animal参数。

多态实现的原理

多态实现的原理

多态实现的原理嘿,同学们!今天咱们来聊聊一个有点难理解,但其实也挺好玩的事儿,那就是多态实现的原理。

啥是多态呢?听着好像很复杂的样子,其实呀,多态就像是一个魔法盒子,里面有好多不同的东西,但是它们都能做一些相似的事情。

比如说,咱们班里有很多同学,大家都能画画,但是每个人画的画都不一样。

这就有点像多态啦,虽然做的事情都是画画,但是画出来的结果各不相同。

那多态是怎么实现的呢?这就有点像变魔术啦。

首先呢,得有一些不同的东西,就像咱们有不同的同学一样。

在电脑的世界里,这些不同的东西可以是不同的程序或者代码。

比如说,有一个游戏程序,里面有不同的角色,每个角色都有自己的特点和能力。

然后呢,这些不同的东西要有一个共同的地方,就像咱们同学都能画画一样。

在程序里,这个共同的地方可以是一个方法或者一个功能。

比如说,这些不同的角色都能走路,这个走路的动作就是它们的共同之处。

但是呢,每个角色走路的方式又不一样。

有的角色走得快,有的角色走得慢,有的角色还会跳着走。

这就是多态的神奇之处啦。

在程序里,不同的角色实现走路这个功能的方法是不一样的。

那怎么做到这样的呢?这就需要一种特别的魔法,叫做继承和重写。

继承呢,就像是咱们从爸爸妈妈那里继承一些特点一样。

在程序里,一个角色可以从另一个更基础的角色那里继承一些功能。

比如说,有一个基础角色叫“动物”,它会走路。

然后有一个角色叫“兔子”,它继承了“动物”的走路功能,但是它走得特别快。

重写呢,就是把继承来的功能改一改,变成自己独特的样子。

就像咱们同学可以在画画的时候加上自己的创意,让画变得不一样。

兔子继承了动物的走路功能后,可以重写这个走路的方法,让自己走得更快。

这样,通过继承和重写,就实现了多态。

不同的角色可以有不同的表现,但又都能做一些共同的事情。

同学们,你们想想看,多态是不是很有趣呢?就像我们在生活中也有很多多态的例子哦。

比如说,我们都能吃饭,但是每个人喜欢吃的东西不一样;我们都能玩游戏,但是每个人玩游戏的方式不一样。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C++多态的实现原理1. 用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数。

2. 存在虚函数的类都有一个一维的虚函数表叫做虚表。

类的对象有一个指向虚表开始的虚指针。

虚表是和类对应的,虚表指针是和对象对应的。

3. 多态性是一个接口多种实现,是面向对象的核心。

分为类的多态性和函数的多态性。

4. 多态用虚函数来实现,结合动态绑定。

5. 纯虚函数是虚函数再加上= 0。

6. 抽象类是指包括至少一个纯虚函数的类。

纯虚函数:virtual void breathe() = 0;即抽象类!必须在子类实现这个函数!即先有名称,没内容,在派生类实现内容!我们先看一个例子:[cpp] view plaincopy1. #include <iostream>2.3. using namespace std;4.5. class animal6. {7. public:8. void sleep()9. {10. cout<<"animal sleep"<<endl;11. }12. void breathe()13. {14. cout<<"animal breathe"<<endl;15. }16. };17. class fish:public animal18. {19. public:20. void breathe()21. {22. cout<<"fish bubble"<<endl;23. }24. };25. void main()26. {27. fish fh;28. animal *pAn=&fh; // 隐式类型转换29. pAn->breathe();30. }注意,在例1-1的程序中没有定义虚函数。

考虑一下例1-1的程序执行的结果是什么?答案是输出:animal breathe我们在main()函数中首先定义了一个fish类的对象fh,接着定义了一个指向animal 类的指针变量pAn,将fh的地址赋给了指针变量pAn,然后利用该变量调用pAn->breathe()。

许多学员往往将这种情况和C++的多态性搞混淆,认为fh实际上是fish类的对象,应该是调用fish类的breathe(),输出“fish bubble”,然后结果却不是这样。

下面我们从两个方面来讲述原因。

1、编译的角度C++编译器在编译的时候,要确定每个对象调用的函数(要求此函数是非虚函数)的地址,这称为早期绑定(early binding),当我们将fish类的对象fh的地址赋给pAn时,C++编译器进行了类型转换,此时C++编译器认为变量pAn保存的就是animal对象的地址。

当在main()函数中执行pAn->breathe()时,调用的当然就是animal对象的breathe函数。

2、内存模型的角度我们给出了fish对象内存模型,如下图所示:我们构造fish类的对象时,首先要调用animal类的构造函数去构造animal类的对象,然后才调用fish类的构造函数完成自身部分的构造,从而拼接出一个完整的fish对象。

当我们将fish类的对象转换为animal类型时,该对象就被认为是原对象整个内存模型的上半部分,也就是图1-1中的“anima l的对象所占内存”。

那么当我们利用类型转换后的对象指针去调用它的方法时,当然也就是调用它所在的内存中的方法。

因此,输出animal breathe,也就顺理成章了。

正如很多学员所想,在例1-1的程序中,我们知道pAn实际指向的是fish类的对象,我们希望输出的结果是鱼的呼吸方法,即调用fish类的breathe方法。

这个时候,就该轮到虚函数登场了。

前面输出的结果是因为编译器在编译的时候,就已经确定了对象调用的函数的地址,要解决这个问题就要使用迟绑定(late binding)技术。

当编译器使用迟绑定时,就会在运行时再去确定对象的类型以及正确的调用函数。

而要让编译器采用迟绑定,就要在基类中声明函数时使用virtual关键字(注意,这是必须的,很多学员就是因为没有使用虚函数而写出很多错误的例子),这样的函数我们称为虚函数。

一旦某个函数在基类中声明为virtual,那么在所有的派生类中该函数都是virtual,而不需要再显式地声明为virtual。

下面修改例1-1的代码,将animal类中的breathe()函数声明为virtual,如下:[cpp] view plaincopy1. #include <iostream>2.3. using namespace std;4.5. class animal6. {7. public:8. void sleep()9. {10. cout<<"animal sleep"<<endl;11. }12. virtual void breathe()13. {14. cout<<"animal breathe"<<endl;15. }16. };17.18. class fish:public animal19. {20. public:21. void breathe()22. {23. cout<<"fish bubble"<<endl;24. }25. };26.27. void main()28. {29. fish fh;30. animal *pAn=&fh; // 隐式类型转换31. pAn->breathe();32. }大家可以再次运行这个程序,你会发现结果是“fish bubble”,也就是根据对象的类型调用了正确的函数。

那么当我们将breathe()声明为virtual时,在背后发生了什么呢?编译器在编译的时候,发现animal类中有虚函数,此时编译器会为每个包含虚函数的类创建一个虚表(即vtable),该表是一个一维数组,在这个数组中存放每个虚函数的地址。

对于例1-2的程序,animal和fish类都包含了一个虚函数breathe(),因此编译器会为这两个类都建立一个虚表,(即使子类里面没有virtual函数,但是其父类里面有,所以子类中也有了)如下图所示:那么如何定位虚表呢?编译器另外还为每个类的对象提供了一个虚表指针(即vptr),这个指针指向了对象所属类的虚表。

在程序运行时,根据对象的类型去初始化vptr,从而让vptr正确的指向所属类的虚表,从而在调用虚函数时,就能够找到正确的函数。

对于例1-2的程序,由于pAn实际指向的对象类型是fish,因此vptr指向的fish类的vtable,当调用pAn->breathe()时,根据虚表中的函数地址找到的就是fish类的breathe()函数。

正是由于每个对象调用的虚函数都是通过虚表指针来索引的,也就决定了虚表指针的正确初始化是非常重要的。

换句话说,在虚表指针没有正确初始化之前,我们不能够去调用虚函数。

那么虚表指针在什么时候,或者说在什么地方初始化呢?答案是在构造函数中进行虚表的创建和虚表指针的初始化。

还记得构造函数的调用顺序吗,在构造子类对象时,要先调用父类的构造函数,此时编译器只“看到了”父类,并不知道后面是否后还有继承者,它初始化父类对象的虚表指针,该虚表指针指向父类的虚表。

当执行子类的构造函数时,子类对象的虚表指针被初始化,指向自身的虚表。

对于例2-2的程序来说,当fish类的fh对象构造完毕后,其内部的虚表指针也就被初始化为指向fish 类的虚表。

在类型转换后,调用pAn->breathe(),由于pAn实际指向的是fish类的对象,该对象内部的虚表指针指向的是fish类的虚表,因此最终调用的是fish类的breathe()函数。

要注意:对于虚函数调用来说,每一个对象内部都有一个虚表指针,该虚表指针被初始化为本类的虚表。

所以在程序中,不管你的对象类型如何转换,但该对象内部的虚表指针是固定的,所以呢,才能实现动态的对象函数调用,这就是C++多态性实现的原理。

总结(基类有虚函数):1. 每一个类都有虚表。

2. 虚表可以继承,如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现。

如果基类有3个虚函数,那么基类的虚表中就有三项(虚函数地址),派生类也会有虚表,至少有三项,如果重写了相应的虚函数,那么虚表中的地址就会改变,指向自身的虚函数实现。

如果派生类有自己的虚函数,那么虚表中就会添加该项。

3. 派生类的虚表中虚函数地址的排列顺序和基类的虚表中虚函数地址排列顺序相同。

这就是C++中的多态性。

当C++编译器在编译的时候,发现animal类的breathe()函数是虚函数,这个时候C++就会采用迟绑定(late binding)技术。

也就是编译时并不确定具体调用的函数,而是在运行时,依据对象的类型(在程序中,我们传递的fish类对象的地址)来确认调用的是哪一个函数,这种能力就叫做C++的多态性。

我们没有在breathe()函数前加virtual关键字时,C++编译器在编译时就确定了哪个函数被调用,这叫做早期绑定(early binding)。

C++的多态性是通过迟绑定技术来实现的。

C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。

如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。

虚函数是在基类中定义的,目的是不确定它的派生类的具体行为。

例:定义一个基类:class Animal//动物。

它的函数为breathe()//呼吸。

再定义一个类class Fish//鱼。

它的函数也为breathe()再定义一个类class Sheep //羊。

它的函数也为breathe()为了简化代码,将Fish,Sheep定义成基类Animal的派生类。

然而Fish与Sheep的breathe不一样,一个是在水中通过水来呼吸,一个是直接呼吸空气。

所以基类不能确定该如何定义breathe,所以在基类中只定义了一个virtual breathe,它是一个空的虚函数。

具本的函数在子类中分别定义。

程序一般运行时,找到类,如果它有基类,再找它的基类,最后运行的是基类中的函数,这时,它在基类中找到的是virtual标识的函数,它就会再回到子类中找同名函数。

相关文档
最新文档