c++11多态性与虚函数
虚函数原理
虚函数原理虚函数是 C++ 中一个非常重要的特性,它为面向对象编程提供了很强的支持。
虚函数的实现原理是通过虚函数表实现的,本文将介绍虚函数的概念、使用方法以及实现原理。
一、虚函数概念虚函数是指在基类中使用 virtual 关键字声明的成员函数,它的作用是允许在子类中对该函数进行覆盖。
具体来说,虚函数允许在子类中定义一个与基类中同名的函数,当使用子类对象调用该函数时,程序会动态的选择调用子类中的函数。
虚函数的语法如下:```class Base {public:virtual void foo();};```虚函数可以被重写(覆盖),也可以被继承,但是不能被 static 和 friend 修饰。
二、虚函数的使用使用虚函数需要满足一下条件:1.虚函数必须在公有的类成员函数列表中声明,并在类声明的内部定义。
2.虚函数必须在基类和派生类中以相同的参数列表进行定义。
下面是一个使用虚函数的简单例子:class Square: public Shape {public:Square(double s) : side(s) {}double getArea() { return side * side; }Shape 是一个基类,Square 是它的一个派生类,Square 中重写了 getArea() 函数,计算正方形的面积。
虚函数的实现原理是通过虚函数表实现的。
虚函数表是一个指针数组,存储了每个类中的虚函数指针。
当对象被创建时,会在其内存空间中创建一个指向虚函数表的指针,这个指针通常称为虚函数表指针(vptr),虚函数的调用就是通过这个指针完成的。
每个含有虚函数的类都有一个独立的虚函数表,虚函数表智能在类的第一个对象中存储,它包含了该类中所有虚函数的地址。
在派生类中,虚函数表通常继承自它的直接基类,并在此基础上添加或修改虚函数的地址。
这样如果在派生类对象中调用虚函数时,程序会先获得对象的虚函数表指针,然后通过该指针找到对应的虚函数地址来执行函数。
C++11新特性总结
C++11新特性总结前⾔转载请注明出处,感谢!C++11 的新特性1 变量和基本类型1.1 long long 类型扩展精度浮点数,10位有效数字1.2 列表初始化初始化的⼏种不同形式,其中⽤花括号来初始化变量称为列表初始化;⽐如:int i = 0;int i = {0};int i{0};int i(0);需要注意的是,当⽤于内置类型的变量时,这种初始化形式有⼀个重要的特点:如果我们使⽤初始化且初始值存在丢失信息的风险,则编译器报错;例如:long double ld = 3.1414141414;int a{ld}, b = {ld}; //报错int c(ld), d = ld; //正确cout << "a:" << a << "b:" << b << "c:" << c << "d" << d << endl;运⾏的时候,a,b则会提⽰报错信息:error: type 'long double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing],这是因为使⽤long double的值初始化int变量时可能会丢失数据,所以拒绝a和b的初始化请求;虽然c,d虽然没有报错,但是确实丢失了数据;[这是为什么?]1.3 nullptr 常量有⼏种⽣成空指针的⽅法:int *p1 = nullptr; // 等价于int *p1 = 0;int *p2 = 0;int *p3 = NULL; // 等价于int *p3 = 0;在新标准下,建议尽量使⽤新标准nullptr,nullptr是⼀种特殊类型的字⾯值,它可以被转换成任意其它的指针类型,虽然其它的⽅式也是可以的;1.4 constexpr 变量将变量声明为constexpr类型以便由编译器来验证变量的值是否是⼀个常量表达式;声明为constexpr的变量⼀定是⼀个常量,⽽且必须⽤常量表达式来初始化,⽐如说下⾯的情况则是不正确的:int t = 10;constexpr int q = t + 20;cout << "q" << q << endl;需要将t声明为const才是正确的;⼀般来说,如果你认定变量是⼀个常量表达式,那就把它声明为constexpr类型;1.5 类型别名声明使⽤类型别名可以使复杂的类型名字变得更简单明了,易于理解和使⽤;现在有两种⽅法可以⽤来定义类型别名,⼀种是typedef,另⼀种则是新标准中的using;#include <iostream>using namespace std;int add(int val) {return 10 + val;}int main() {typedef double dnum;// 字符指针typedef char *pstring;// 函数// 返回值类型为int,参数类型为int的函数typedef int func(int);// 函数指针,指向返回值类型为int,参数类型为int的函数typedef int (*pfunc)(int);// 函数引⽤,指向返回值类型为int,参数类型为int的函数typedef int (&tfunc)(int);pfunc pfunc_add = nullptr;pfunc_add = add;cout << "函数指针,result is " << pfunc_add(10) << endl;tfunc tfunc_add = add;cout << "函数引⽤,result is " << tfunc_add(10) << endl;func &func_add = add; //这⾥使⽤指针或者引⽤都可以cout << "函数,result is " << func_add(10) << endl;// 数组// 元素类型为int,个数为10的数组typedef int arr[10];// 数组指针,指向元素类型为int,个数为10的数组typedef int (*parr)[10];// 数组引⽤,绑定到元素类型为int,个数为10的数组typedef int (&tparr)[10];using dnum2 = double;using pstring2 = char*;using func2 = int(int);using pfunc2 = int(*)(int);using arr2 = int[10];using parr2 = int(*)[10];using tparr2 = int(&)[10];std::cout << "Hello, World!" << std::endl;return 0;}但是需要注意的是,如果某个类型别名指代的是复合类型或者常量,那么就会产⽣意想不到的后果;⽐如说typedef char *pstring;const pstring cstr = 0;按照我们正常的理解就是,将char*替换掉pstring,得到 const char* cstr;然⽽事实是pstring是⼀个字符指针,其基本数据类型是个指针,此时⽤此字符指针去声明cstr,得到的是⼀个常量的字符指针,但是按照本意是指向char的常量指针,其基本类型是char,也就是说两者修饰的东西是不⼀样的!但是如果是引⽤变量则没有关系。
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 }。
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++语言中的虚函数研究作者:徐启丰, 胡勇, 万玉成, XU Qifeng, HU Yong, WANG Yucheng作者单位:徐州空军学院,江苏,徐州,221000刊名:现代电子技术英文刊名:MODERN ELECTRONICS TECHNIQUE年,卷(期):2010,33(4)参考文献(14条)1.Stanley B Lippman;侯捷Inside the C++ Object Model 20012.蓝雯飞;陆际光C++面向对象程序设计中的多态性研究[期刊论文]-计算机工程与应用 2000(08)3.Bjarne Stroustrup;裘宗燕C++的设计与演化 20024.赵红超;方金云;唐志敏C++的动态多态和静态多态[期刊论文]-计算机工程 2005(20)5.蓝雯飞C++中的多态性及其应用 1998(07)6.袁亚丽;肖桂云C++中虚函数的实现技术研究[期刊论文]-河北北方学院学报(自然科学版) 2006(05)7.Scott Mayers More Effective C++ 19968.和力;吴丽贤关于C++虚函数底层实现机制的研究与分析[期刊论文]-计算机工程与设计 2008(10)9.亚鹏关于C++中虚函数的几个问题 2006(02)10.Terrence W Pratt;傅育熙程序设计语言:设计与实现 200111.张昀C++中的多态性研究 2009(02)12.Bjarne Stroustrup The C++ Programming Language 200113.夏承遗;董玉涛;赵德新C++中虚函数的实现机制[期刊论文]-天津理工学院学报 2004(03)14.蓝雯飞C++语言中的面向对象特征探讨[期刊论文]-计算机工程与应用 2000(09)本文链接:/Periodical_xddzjs201004048.aspx。
【C++面向对象的程序设计】6多态性
虚析构函数
析构函数的作用是对象撤销之后清理现场。 在派生类对象撤销时,一般先调用派生类的 析构函数。再调用基类的析构函数。
然而,当定义的是一个指向基类的指针变量, 使用new运算符建立临时对象时,如果基类 中有析构函数,则在使用delete析构时只会 调用基类的析构函数。
这就需要将基类中的析构函数声明为虚函数。
虚函数的声明与使用
声明虚函数的一般格式如下: virtual 函数原型;
⑴ 必须首先在基类中声明虚函数。 ⑵ 派生类中与基类虚函数原型完全相同的成员函 数,即使在说明时前面没有冠以关键字virtual也 自动成为虚函数。
声明虚函数
⑶ 只有非静态成员函数可以声明为虚函数。 ⑷ 不允许在派生类中定义与基类虚函数名字及参数 特征都相同,仅仅返回类型不同的成员函数。 编译时 出错。 ⑸ 系统把函数名相同但参数特征不同的函数视为不 同的函数。 ⑹ 通过声明虚函数来使用C++提供的多态性机制时, 派生类应该从它的基类公有派生。
构函数等内容。
本章内容
静态联编与动态联编 虚函数的声明与使用 纯虚函数和抽象类 虚析构函数
Hale Waihona Puke 静态联编与动态联编所谓联编(tinding),就是使一个计算机程序的不同部 分彼此关联的过程。
静态联编在编译阶段完成,因为所有联编过程都在程 序开始运行之前完成,因此静态联编也叫先前联编或早期 联编。
另一种情况编译程序在编译时并不确切知道应把发送 到对象的消息和实现消息的哪段具体代码联编在一起,而 是在运行时才能把函数调用与函数体联系在一起,则称为 动态联编。
动态联编的实现
C ++语言中的动态联编是通过使用虚函数表 (Virtual Function Table)来实现的,虚函数表也称 为v-表。
c++三大特征的理解
c++三大特征的理解
C++语言的三大特征是封装、继承和多态。
封装是指将数据和操作数据的方法捆绑在一起,以防止外部访
问和不合法修改,从而保证数据的安全性和一致性。
通过封装,可
以隐藏实现细节,使得对象更容易被使用,并且减少了对外部的依赖。
继承是指一个类可以派生出新的类,新的类可以继承原有类的
属性和方法,并且可以添加新的属性和方法。
继承可以提高代码的
复用性,减少重复编码,同时也能够实现多态性。
多态性是指同一个消息被不同的对象接收时,可以产生不同的
行为。
在C++中,多态性可以通过虚函数和纯虚函数来实现。
多态
性使得程序更加灵活,能够根据不同对象的类型来执行不同的操作,从而提高了代码的可扩展性和可维护性。
总的来说,封装、继承和多态是C++语言的三大特征,它们为
面向对象编程提供了强大的支持,使得程序更加模块化、灵活和易
于扩展。
【大学】C++面向对象程序设计 多态性与虚函数
调用不同的类(基类或派生类)的虚函数,从而完成不同的功能,这
又是一种多态性的体现。
.
蚌埠学院计算机系 4
C++面向对象程序设计
9.1.2 静态多态性和动态多态性
编译时多态通过静态联编实现,运行时多态通过动态联 编实现。
1 联编 在面向对象程序设计中,联编(binding)的含义是把
一个函数名与其实现的代码联系在一起,即主调函数代码 必须与被调函数代码连接起来。
.
蚌埠学院计算机系 12
C++面向对象程序设计
9.2 对虚函数的限制
9.2.1 声明虚函数的限制
一般情况下,可将类中具有共性的成员函数声明为虚函数,而个 性的函数往往为某一个类独有,声明为一般成员函数。将类的成员函 数声明为虚函数有利于编程,但下面的函数不能声明为虚函数:
⑴构造函数不能声明为虚函数。构造函数在对象创建时调用,完成对象 的初始化,此时对象正在创建中,基类指针无从指向。只有在构造过 程完成后,对象才存在,才能被基类指针指向。
9.1.1 多态性的实现方法
同一段代码,当用不同的对象去调用时,该代码具有不同的功能,这 称为多态性。C++提供的多态性分为静态多态性(编译时多态)和动 态多态性(运行时多态)。静态多态性是一种编译时的多态,是通过 重载和模板实现的。动态多态性是一种运行时的多态,其基础是数据 封装和继承机制,通过继承建立类层次,并通过在基类中定义虚函数 来实现多态性,即在基类和派生类中建立同名的函数,但是函数的功 能是不同的。
2 静态多态性
在没有类层次的场合,使用函数重载的方式实现静态多态性。 各个重载函数名称相同,但参数表应在参数个数、类型和次序 上有所不同。编译器根据参数表来识别各个重载函数。根据参 数表,系统在编译时就完成静态联编的过程。关于没有类层次 的函数重载实现多态的例子前面已经介绍,这里不再赘述。
c++课件
上一页 下一页
返回
9.2
多态性与虚函数
Point P1(10,10); Circle C1(10,10,20); Point *Pp=&P1; fun(Pp); Pp=&C1; fun(Pp); } 程序中类Point和类Circle属于同一个类族,而且是通过公有派生而来, 因此满足类型兼容规则,同时,基类Point的成员函数Area()声明为虚 函数,程序中使用对象指针来访问成员函数,这样联编就是在运行中 完成,实现了运行时的多态。通过基类的指针就可以访问到正被指向 的对象的成员。这样,能够对同一类族中的对象进行统一的处理,抽 象程度更高,程序更简洁、更高效。程序的运行结果为:
上一页 下一页
返回
9.2
多态性与虚函数
double radius; public: Circle(int x,int y,double r):Point(x,y){ radius=r; } double Area(){ return PI*radius*radius; } }; void fun(Point* p){ cout<<p->Area()<<endl; } void main(){
上一页 下一页
返回
9.1 多态性概述
调用与执行该操作代码的关系,即确定某一个同名标识到底是要调用 哪一段程序代码。有些多态类型,其同名操作的具体对象能够在编译、 连接阶段确定,通过静态联编解决,比如运算符重载、函数模板实例 化以及强制和参数多态都属于静态联编。 和静态联编相对应,在编译、连接过程中无法解决的联编问题,要等 到程序开始运行之后再来确定,称为动态联编,也称为晚期联编或后 联编。包含多态操作对象的确定就是通过动态联编完成的。
虚 函 数
(6)一个虚函数无论被公有继承多少次,它仍然保持其 虚函数的特性。
my_base--------
10 20
从程序运行的结果可以看出,虽然执行语句mp=&mc;后, 指针mp已经指向了对象mc,但是它所调用的函数(mp>show()),仍然是其基类对象的show(),显然这不是我 们所期望的。出现这个问题的原因以及解决方法,我 们将在下一节介绍。在此先说明引入派生类后,使用 对象指针应注意的几个问题:
derive op2; //定义派生类derive的对象op2
ptr=&op1;
//使指针ptr指向对象op1
ptr=&op2;
//错误,不允许将base类指针ptr指
向它的私有派生类//对象op2
//…
}
(2)允许将一个声明为指向基类的指针指向公有派生类
的对象,但是不能将一个声明为指向派生类对象的指 针指向其基类的对象。
(3)声明为指向基类的指针,当其指向公有派生的
对象时,只能用它来直接访问派生类中从基类继承来
的成员,而不能直接访问公有派生类中定义的成员,
例如:
class A { //... public: void print1(); }; class B:public A{ //... public: print2(); };
可见,虚函数同派生类的结合和使C++支持运行时的多 态性,而多态性对面向对象的程序设计是非常重要的, 实现了在基类定义派生类所拥有的通用接口,而在派 生类定义具体的实现方法,即常说的“同一接口,多 种方法”。
c++11面试知识点总结
c++11面试知识点总结1.移动语义和右值引用:C++11引入了右值引用和移动语义,通过&&修饰,可以使得对象的值被“移动”而不是“复制”,提高性能。
2.智能指针:C++11引入了unique_ptr和shared_ptr两种智能指针,可以自动管理动态分配的内存,避免内存泄漏和悬挂指针问题。
3.多线程:C++11引入了线程库,包括线程创建、同步、互斥等功能,使得多线程编程更加方便和高效。
mbda表达式:C++11引入了lambda表达式,可以方便地定义匿名函数,简化代码,提高代码可读性。
5.数据类型增强:C++11引入了auto关键字,可以自动推导变量的类型;还增加了nullptr和enum class等用于提高代码的可靠性和可读性的特性。
6.统一初始化语法:C++11引入了统一初始化语法,即可以用“{}”来初始化对象,可以避免窄化转换和隐式类型转换的问题。
7.右值引用相关的特性:C++11引入了移动构造函数和移动赋值运算符,可以提高对象的效率;还引入了完美转发和转移语义等,使得代码更加灵活和高效。
8.并发编程:C++11在语言层面上增加了并发编程的支持,包括原子操作、互斥量、条件变量等,可以实现线程间的同步和通信。
9.异常处理:C++11引入了新的异常处理机制,包括了无异常声明的函数、异常类型的推导等,使得异常处理更加安全和高效。
10.容器和算法增强:C++11引入了新的容器和算法,如unordered_map、unordered_set、移动语义可用的容器和算法等,提高了效率和代码的可读性。
以上是C++11面试的一些核心知识点,面试时可以根据具体情况深入了解和准备。
深入理解C++11---C++ 11新特性解析与应用(摘录)
深入理解C++11:C++ 11新特性解析与应用(摘录)final/override控制在了解C++11中的final/override关键字之前,我们先回顾一下C++关于重载的概念。
简单地说,一个类A中声明的虚函数fun在其派生类B中再次被定义,且B中的函数fun跟A中fun的原型一样(函数名、参数列表等一样),那么我们就称B重载(overload)了A的fun函数。
对于任何B类型的变量,调用成员函数fun 都是调用了B重载的版本。
而如果同时有A的派生类C,却并没有重载A的fun函数,那么调用成员函数fun则会调用A中的版本。
这在C++中就实现多态。
在通常情况下,一旦在基类A中的成员函数fun被声明为virtual的,那么对于其派生类B而言,fun总是能够被重载的(除非被重写了)。
有的时候我们并不想fun在B类型派生类中被重载,那么,C++98没有方法对此进行限制。
我们看看下面这个具体的例子,如代码清单2-23所示。
代码清单2-23#include <iostream>using namespace std;classMathObject{public:virtual double Arith() = 0;virtual void Print() = 0;};class Printable : public MathObject{public:double Arith() = 0;void Print() // 在C++98中我们无法阻止该接口被重写{cout<< "Output is: " <<Arith() <<endl;}};class Add2 : public Printable {public:Add2(double a, double b): x(a), y(b) {}double Arith() { return x + y; }private:double x, y;};class Mul3 : public Printable {public:Mul3(double a, double b, double c): x(a), y(b), z(c) {}double Arith() { return x * y * z; }private:double x, y, z;};// 编译选项:g++ 2-10-1.cpp在代码清单2-23中,我们的基础类MathObject定义了两个接口:Arith和Print。
虚函数的用法
虚函数的用法
虚函数是面向对象编程中的一个重要概念。
它允许子类重写父类中的同名函数,以实现多态性。
在C++中,使用关键字"virtual"来声明一个函数为虚函数。
虚函数的使用有以下几个关键点:
1. 多态性:虚函数的主要作用是实现多态性。
当一个指向父类的指针或引用调用一个虚函数时,实际执行的是子类中重写的函数。
这种行为允许在运行时根据对象的实际类型来确定调用的函数。
2. 动态绑定:虚函数的调用是动态绑定的,也就是说在运行时确定具体调用的函数。
与之相反的是静态绑定,它是在编译时确定调用的函数。
动态绑定使得程序具有更大的灵活性和扩展性。
3. 虚函数表:为了实现动态绑定,编译器会为每个包含虚函数的类创建一个虚函数表(vtable)。
虚函数表是一个存储函数指针的数组,每个函数指针指向对应虚函数的实际实现。
每个对象都有一个指向其类的虚函数表的指针,通过这个指针可以实现动态调用。
4. 纯虚函数:有时候父类中的虚函数并不需要有实际的实现,而只
是为了在子类中进行重写。
这种函数被称为纯虚函数,可以通过在函数声明中添加"= 0" 来表示。
包含纯虚函数的类被称为抽象类,它只能作为基类使用,不能被实例化。
综上所述,虚函数是实现多态性的关键机制之一。
通过在父类中声明虚函数并在子类中重写,可以实现基于对象实际类型的动态绑定,提高程序的灵活性和可扩展性。
C++继承,虚函数与多态性专题
本文作者:黄邦勇帅学习本文首先你应熟悉C++中的构造函数,基本的类的声明及怎样初始化类,关于这些问题,请参看本人所作的《C++构造函数,复制构造函数和析构函数》一文,在这篇文章中作了详细的介绍。
本文分两部分即继承和虚函数与多态性,本文第一部分详细讲解了继承时的构造函数和析构函数的问题,父类与子类的同名变量和函数问题,最后介绍了多重继承与虚基类。
本文第二部分重点介绍了虚函数与多态性的问题,因此学习虚函数的基础是继承,因此在学习虚函数前应学好继承。
本文详细易懂,内容全面,是学习C++的不错的资料。
本文内容完全属于个人见解与参考文现的作者无关,其中难免有误解之处,望指出更正。
声明:禁止抄袭本文,若需要转载本文请注明转载的网址,或者注明转载自“黄邦勇帅”。
主要参考文献:1、C++.Primer.Plus.第五版.中文版[美]Stephen Prata著孙建春韦强译人民邮电出版社2005年5月2、C++.Primer.Plus.第四版.中文版Stanley B.Lippman、Barbara E.Moo著李师贤等译人民邮电出版社2006年3月3、C++.Primer.Plus.第三版.中文版Stanley B.Lippman等著潘爱民张丽译中国电力出版社2002年5月4、C++入门经典第三版[美]Ivor Horton著李予敏译清华大学出版社2006年1月5、C++参考大全第四版[美]Herbert Schidt著周志荣朱德芳于秀山等译电子工业出版社2003年9月6、21天学通第四版C++ [美]Jesse Liberty著康博创作室译人民邮电出版社2002年3月14 继承(基类,父类,超类),派生类,子类一:继承中的访问权限关系。
1.基类,父类,超类是指被继承的类,派生类,子类是指继承于基类的类.2.在C++中使用:冒号表示继承,如class A:public B;表示派生类A从基类B继承而来3.派生类包含基类的所有成员,而且还包括自已特有的成员,派生类和派生类对象访问基类中的成员就像访问自已的成员一样,可以直接使用,不需加任何操作符,但派生类仍然无法访问基类中的私有成员.4.在C++中派生类可以同时从多个基类继承,Java不充许这种多重继承,当继承多个基类时,使用逗号将基类隔开.5.基类访问控制符,class A:public B基类以公有方式被继承,A:private B基类以私有方式被继承,A:protected B基类以受保护方式被继承,如果没有访问控制符则默认为私有继承。
虚函数作用
虚函数作用
虚函数是C++中的一个重要概念,它允许派生类重写基类的方法并实现多态性。
在使用虚函数时,我们必须在基类中将函数声明为虚函数,并在派生类中重新定义该函数。
这种机制使得基类指针可以指向派生类对象,并且能够调用派生类中重写的方法。
虚函数的作用非常重要,它可以帮助我们实现多态性。
多态性是面向对象编程的一个非常重要的概念,它允许我们在不知道对象实际类型的情况下调用其方法。
这种方法的调用是通过基类指针实现的,而派生类中的方法实现则通过虚函数来实现。
虚函数还可以帮助我们实现动态绑定。
动态绑定是将函数调用和函数实现联系起来的过程。
在C++中,函数调用是在编译时决定的,而函数实现是在运行时决定的。
因此,我们需要使用虚函数来实现动态绑定,以便在运行时确定正确的方法调用。
虚函数还可以帮助我们实现抽象类和接口。
抽象类是不能被实例化的类,它只是一个用于派生其他类的基类。
抽象类中可以包含纯虚函数,这些函数没有实现,必须在派生类中实现。
接口是一组纯虚函数的集合,它表示了一个类所提供的接口。
这些函数在接口类中没有实现,必须在实现类中实现。
总之,虚函数在C++中具有非常重要的作用。
它可以帮助我们实现多态性、动态绑定、抽象类和接口。
对于面向对象编程来说,虚函数是非常重要的概念之一,我们需要深入理解并熟练运用。
- 1 -。
《C++程序设计》电子教案第9章 多态性和虚函数
返回本节
9.1.3 派生类指针
指向基类和派生类的指针是相关的。 例如: A * p ; // 指向类型 A 的对象的指针 A A_obj ; // 类型 A 的对象 B B_obj ; // 类型 B 的对象 p = & A_obj ; // p 指向类型 A 的对象 p = & B_obj ; // p 指向类型 B 的对象,它是 A 的派生类
((B_class *)p)->show_phone( ); // 用基类指针访问公有派生类的特定成员,必须进行类型转换 } 此程序的运行结果为: Zhang San Li Si
0731_12345678 0731_12345678
例9-9:写出下面的程序的执行结果。 #include <iostream.h> class Student { public: Student(int xx) { x=xx; } virtual float calcTuition( ); protected: int x; }; float Student::calcTuition() { return float(x*x);
main ( ) { A_class *p; //对象指针 A_class A_obj; //对象 B_class * bp; B_class B_obj; p=&A_obj; //P指针指向基类对象,调用基类成员函数 p->put_name("Zhang San"); p=& B_obj; //P指针指向派生类对象,调用继承自基类的成 员函数 p->put_name("Li Si"); A_obj.show_name( ); B_obj.show_name( ); bp=&B_obj; bp->put_phone("0731_12345678"); bp->show_phone( );
《C++面向对象程序设计》 谭浩强 第六章
C++
先声明基类point类,并调试之; 再声明派生类circle类,调试之; 最后声明cylinder类,调试之。
6- 5
① 先声明基类point
#include <iostream.h> class point //声明类point {public: point (float x=0, float y=0 ); //有默认值的构造函数 void setPoint (float, float); //设置点坐标 float getX ( ) const { return x; } // 读x 值 float getY ( ) const { return y; } // 读y 值 friend ostream & operator << ( ostream &, const point &); protected: float x,y; }; // 下面定义 point类的成员函数 point :: point (float a, float b) // point的构造函数 { x = a; y = b; } void point :: setPoint (float a, float b) { x =a; y = b; } ostream &operator << (ostream &output, const point &p) { // 重载运算符<< output<<“[x=“<<p.x<<“, y=“<<p.y<<“]”<<endl; return output; }
6Hale Waihona Puke 7③ 最后声明cylinder类
C++程序设计基础第6章 虚函数与多态性
6.2.1 虚函数的定义
2. 虚函数的定义 • 虚函数的定义是在基类中进行的 。 • 虚函数的定义语法格式如下:
virtual<函数类型><函数名>(形参表) {
函数体 }
12
6.2.1 虚函数的定义
3. 定义虚函数时,要注意遵循以下规则: 1)只有成员函数才能声明为虚函数,因为虚
函数仅适用于有继承关系的类对象,所以 普通函数不能声明为虚函数。 2)虚函数的声明只能出现在类声明中的函数 原型声明中,而不能出现在成员的函数体 实现上。 3)类的静态成员函数不可以定义为虚函数, 因为静态成员函数不受限于某个对象。
}
7
void main()
{
MaxClass mc(34,67,143,89);
cout<<"计算前两个数中的最大数为:“
<<mc.max(34,67)<<endl;
cout<<"计算前三个数中的最大数为:“
<<mc.max(34,67,143)<<endl;
cout<<"计算四个数中的最大数为:“
运行结果: 张晓强,园林工人 李文卓,生命科学教师
23
6.2.3 虚函数的重载
• 2. 多继承中的虚函数
【例6.8】多继承中使用虚函数例题。
#include <iostream.h>
class base1
//定义基类base1
{
public: virtual void display()
//函数定义为虚函数
运行结果:
(1) : 动物(食草/食肉). (2) : 食草动物 : 羚羊 (3) : 食草动物 : 羚羊 (4) : 食肉动物 : 老虎 (5) : 食肉动物 : 老虎 (6) : 食草动物 : 羚羊 (7) : 食肉动物 : 老虎
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
虚析构函数举例
void main()//虚析构函数 虚析构函数 { Point *p; p=new Circle( 3.5, 22, 8 ); delete p; p=new Rectangle( 10, 20, 100, 200 ); delete p; }
21
基类析构函数为虚函数运行结果 基类析构函数为虚函数运行结果 虚函数
输出各种形状的信息
void outputShape( Shape *shape) { shape->printShapeName(); shape->print(); cout<<...<<shape->area()<<endl; cout<<...<<shape->volume()<<endl; }
switch结构存在的问题 结构存在的问题
添加或删除类时,必须修改画图模块代码: 添加或删除类时,必须修改画图模块代码:
... case 4: triangle.Draw(); break; ...
8
//画图模块 画图模块 //非switch结构解决方案 结构解决方案 非 结构 void drawShape( Point *shape) { shape->draw(); }
28
抽象类: 抽象类:Shape
具体类中必须实现) 纯虚函数 (具体类中必须实现 具体类中必须实现
printShapeName print
派生类中可以重定义) 虚函数 (派生类中可以重定义 派生类中可以重定义
area volume 如果不重定义, 如果不重定义,使用基类定义
29
class Point: public Shape //具体类 具体类 { public: … void printShapeName() const//实现纯虚函数 实现纯虚函数 { cout << "Point: "; } void print() const; //实现纯虚函数 实现纯虚函数 {cout<<"Center=["<<x<<", "<<y<<"]\n";} protected: int x, y; };
void drawShape(int choice)//画图模块 画图模块 { switch(choice)// switch结构解决方案 结构解决方案 结构 { case 1: point.Draw(); break; case 2: circle.Draw(); break; case 3: rect.Draw(); break; } }
解决方法
将基类析构函数声明为virtual, 将基类析构函数声明为virtual,派生类析构函数自 动成为virtual 动成为
当delete时,将会调用合适的析构函数 时
先调用基类指针所指对象的派生类析构函数 后调用基类析构函数
构造函数不能是virtual。为什么? 。为什么? 构造函数不能是
20
程序设计基础(C++) 程序设计基础
软件的扩展——多态性 多态性 软件的扩展
多态性举例
设计一计算机画图程序
现有图形: 现有图形:
Point, Circle, Rectangle等
用户选择某图形
在屏幕绘制该图形
增加新图形: 增加新图形:Triangle
无需改动屏幕画图模块
2
class Point{ public: Point( int = 0, int = 0 ); … void Draw() const; … protected: int x, y; };
程序设计中调用基类的虚成员函数 程序设计中调用基类的虚成员函数 设计 程序运行时调用所指对象所属类的同名成员 程序运行时调用所指对象所属类的同名成员 运行 函数
17
虚函数
在类定义中, 在类定义中,成员函数原型之前添加关键 字virtual 具有继承性,基类中声明了虚函数,派生 具有继承性,基类中声明了虚函数, 类中无论是否说明, 类中无论是否说明,同名成员函数都自动 为虚函数 调用方式:通过基类指针或引用, 调用方式:通过基类指针或引用,执行时 指针 会根据所指的或所引用的实际对象 实际对象, 会根据所指的或所引用的实际对象,决定 动态绑定, 调用相应的成员函数(动态绑定 调用相应的成员函数 动态绑定 binding)
23
Shape
TwoDimensionalShape
ThreeDimensionalShape
Circle
Square
Triangle
Sphere
Cube
Tetrahedron
具体类
可以实例化
定义其对象
实现了其继承的抽象类中的所有纯虚函数
25
定义抽象类
至少有一个纯虚函数 至少有一个纯虚函数
26
非switch结构存在的问题 结构存在的问题
基类指针只能访问基类的成员函数
void printShape( Point *shape) { shape->Draw(); }
11
主要内容
多态性(polymorphism) 多态性 实现多态性:虚函数 实现多态性:
12
class Point{//修改 修改Point类,重新运行程序 修改 类 public: Point( int = 0, int = 0 ); … virtual void Draw() const //虚函数 虚函数 … protected: int x, y; };
double Circle::area() const { return 3.14159 * radius * radius; } void Circle::print() const { Point::print(); cout << "; Radius = " << radius; }
class Cylinder : public Circle //具体类 具体类 { public: … double area() const; double volume() const; void printShapeName() const {cout << "Cylinder: ";} //虚函数 虚函数 void print() const; protected: double height; };
int main()//非switch结构解决方案 结构解决方案 非 结构 { Point point( 7, 11 ); Circle circle( 3.5, 22, 8 ); Rectangle rect( 10, 20, 100, 200 ); Point *shapePtr[]={&point, &circle, &rect }; int choice; do{ cin>>choice; drawShape( shapePtr[choice-1] ); }while(choice); return 0; }
35
本章小结
多态性
设计和实现易于扩展的系统 设计和实现易于扩展的系统 易于扩展 虚函数+基类指针或引用 虚函数 基类指针或引用
需要说明: 需要说明
使用虚函数,系统有一定的空间开销。 使用虚函数,系统有一定的空间开销。当一个 类带有虚函数时, 类带有虚函数时,编译系统会为该类构造一个 虚函数表(vtable),它是一个指针数组,存放 虚函数表 ,它是一个指针数组, 每个虚函数的入口地址。 每个虚函数的入口地址。系统在进行动态关联 时的时间开销是很少的,因此, 时的时间开销是很少的,因此,多态性是高效 的。
Point point( 7, 11 ); Circle circle( 3.5, 22, 8 ); Rectangle rect( 10, 20, 100, 200 ); void main() { int choice; do{ cin>>choice; drawShape(choice); } while(choice); }
14
多态性
使软件易于扩展。 使软件易于扩展。 易于扩展 可以对同一层次结构中所有类的对象 进行统一处理 统一处理。 进行统一处理。
15
多态性
向不同的对象发送相同的消息
通过基类指针或引用
相同的消息具有不同的响应
根据基类指针所指的对象
前提: 前提:同一层次结构
16Leabharlann 现多态性在基类定义虚函数 在基类定义虚函数 在类使用中,通过基类指针 在类使用中,通过基类指针
class Circle : public Point { public: Circle( double r = 0.0, int x = 0, int y = 0 ); … void Draw() const; … protected: double radius; };
class Rectangle : public Point { public: Rectangle( double h = 0.0, double w = 0.0, int x = 0, int y = 0 ); … void Draw() const; … protected: double height; double width; };
double Cylinder::area() const { return 2 * Circle::area() + 2 * 3.14159 * radius * height; } double Cylinder::volume() const { return Circle::area() * height; }