C++中多态实现的关键——虚函数
1.多态的作用
在面向对象程序设计中,使用多态能够增强代码的可扩充性,,即程序需要增加或修改部分功能时,只需修改少量代码就能够达到目的,此外多态也能起到精简代码的作用。
而实现多态的关键点,就是虚函数如何使用。
虚函数
1.虚函数的使用方法
.基同类与派生类同时拥有的同名同参数表的函数,在设计时,最好将其声明为虚函数,只需在函数前面加上关键字virtual即可
下面通过一个具体的例子来说明
#include<iostream>
using namespace std;
class Shape{
public:
void Printf(){
cout<<" The Shape has been called"<<endl;
}
};
class Rectangle:public Shape{
public:
void Printf(){
cout<<"The Rectangle has been called"<<endl;
}
};
class Circle:public Shape{
public:
void Printf(){
cout<<"he Circle has been called"<<endl;
}
};
void Print(Shape &a)
{
a.Printf();
}
void Print(Rectangle &a)
{
a.Printf();
}
void Print(Circle &a)
{
a.Printf();
}
int main()
{
Shape a;
Rectangle b;
Circle c;
Print(a);
Print(b);
Print(c);
return 0;
}
以上程序定义了一个基类Shape 派生出了Rectangle 和 Circle
又定义了三个全局函数用来输出Shape 和Rectangle ,Circle
程序运行结果如下

可以看出三个Print()除了参数,其他完全一样,因此需要一种方法,将Printf()的参数表变得足够灵活,能够同时识别Shape Rectngle Circle
这个时候就可以用到虚函数
具体如下
#include<iostream>
using namespace std;
class Shape{
public:
virtual void Printf(){
cout<<" The Shape has been called"<<endl;
}
};
class Rectangle:public Shape{
public:
void Printf(){
cout<<"The Rectangle has been called"<<endl;
}
};
class Circle:public Shape{
public:
void Printf(){
cout<<"he Circle has been called"<<endl;
}
};
void Print(Shape &a)
{
a.Printf();
}
int main()
{
Shape a;
Rectangle b;
Circle c;
Print(a);
Print(b);
Print(c);
return 0;
}
可以看出,将三个Prinft()函数简化为了一个,而且参数类型为基类的引用
而且在调用时将三种不同的参数类型传入Printf()也不会报错,程序运行结果也是对的
这说明Printf()声明为虚函数后,能够被基类对象正确识别。

并且使用虚函数可以大大简化代码量
2虚函数的识别原理
实际上,对于任何一个含有虚函数,或者其基类含有虚函数的类来说,在其对象的存储空间的最前端,都有一个虚函数表,该虚函数表中存储着该对象的虚函数的地址
多态的函数调用语句被被编译成个根据基类指针或引用所指向的对象中存放的虚函数表的地址,在虚函数表中查询虚函数地址,并调用虚函数的一系列指令

3.虚析构函数
先运行如下程序
#include<iostream>
using namespace std;
class A
{
public:
~A(){
cout<<"A destructor"<<endl;
};
};
class B:public A{
public:
~B(){
cout<<"B destructor"<<endl;
};
};
int main()
{
A *p=new B;
delete p;
return 0;
}

程序的运行结果说明 最后一句delete p,并没有释放掉B的内存,而只释放掉了A的内存,只引发了A的析构函数被调用,被没有调用B的析构函数
这是因为这条语句是静态联编的,编译器进行到delete p 这一句时,并不知道p此时到底指向的是哪个对象,所以只能根据p的类型是A *来调用A的析构函数,
我们希望delete p这样的语句能够足够智能的根据p所指向的对象来执行相应的析构函数
那么将析构函数声明为虚析构函数是一种有效的解决方式
#include<iostream>
using namespace std;
class A
{
public:
virtual ~A(){
cout<<"A destructor"<<endl;
};
};
class B:public A{
public:
~B(){
cout<<"B destructor"<<endl;
};
};
int main()
{
A *p=new B;
delete p;
return 0;
}
将基类析构函数声明为虚函数后
程序运行结果如下

类B的析构函数被正确调用,说明开辟的类B的空间被释放掉
C++中多态实现的关键——虚函数的更多相关文章
- C++中为什么构造函数不能是虚函数,析构函数是虚函数
一, 什么是虚函数? 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离:用形象的语 ...
- C++基础 (7) 第七天 多态的原理 纯虚函数和抽象类 依赖倒置原则
1 昨日回顾 2 多态的原理 1 要有继承 2 要有子类重写父类的虚函数 3 父类指针(或者引用)指向子类对象 (动态联编 虚函数表 3 证明vptr指针的存在 4 vptr指针在构造父类的时候是分步 ...
- C++中的抽象类及纯虚函数的实现与否
1.含有纯虚函数的叫抽象类 2.抽象类(一般是基类)中的纯虚函数无论函数体实现与否,都没有关系,系统会自动忽略 3.继承自抽象类的子类,必须要实现父类的纯虚函数才可以实例化对象 4.抽象类不允许实例化 ...
- c++ 多态问题(在虚函数里调用虚函数)
最近在看cocos2d-x的源码,非常感激cocos2d作者的开源精神.在看代码的过程中感觉两个方向让我受益,1.把之前从书中看到的c++知识,明白了怎么运用.2.学习作者驾驭代码的巧妙方法. 看co ...
- C++中的多态与虚函数的内部实现
1.什么是多态 多态性可以简单概括为“一个接口,多种行为”. 也就是说,向不同的对象发送同一个消息, 不同的对象在接收时会产生不同的行为(即方法).也就是说,每个对象可 ...
- 详解C++中的多态和虚函数
一.将子类赋值给父类 在C++中经常会出现数据类型的转换,比如 int-float等,这种转换的前提是编译器知道如何对数据进行取舍.类其实也是一种数据类型,也可以发生数据转换,但是这种转换只有在 子类 ...
- C++Review1_多态和虚函数
继承是实现多态的基础.虚函数是实现多态的方法.虚函数.多态.继承都是紧密相关的概念.而继承是所有概念的基础: 多态:简单来讲就是接口一样,实现多样.多态是指通过基类的指针或者引用,在运行时动态调用实际 ...
- 【转载】 C++多继承中重写不同基类中相同原型的虚函数
本篇随笔为转载,原文地址:C++多继承中重写不同基类中相同原型的虚函数. 在C++多继承体系当中,在派生类中可以重写不同基类中的虚函数.下面就是一个例子: class CBaseA { public: ...
- 你好,C++(37)上车的人请买票!6.3.3 用虚函数实现多态
6.3.3 用虚函数实现多态 在理解了面向对象的继承机制之后,我们知道了在大多数情况下派生类是基类的“一种”,就像“学生”是“人”类中的一种一样.既然“学生”是“人”的一种,那么在使用“人”这个概念 ...
随机推荐
- 史上最全的Java高级技术点,全是Java高级进阶技术,几乎包含了Java后端的所有知识点
史上最全的Java高级技术点,全是Java高级进阶技术,几乎包含了Java后端的所有知识点 1
- 思科路由器、交换机配置Console 线线序 (亲测可以)
网上有许多标准console线配置线序,在配置思科网络设备时都是不能用的,因为思科的console线序是专用的, 如下 水晶头侧 线序 B 白橙,橙,白绿,蓝 ,白蓝,绿,白粽,棕 对应串口侧线序 1 ...
- Asp.Net Core 3.1 集成Swagger
引入Nuget包 Swashbuckle.AspNetCore.SwaggerGen Swashbuckle.AspNetCore.SwaggerUI 配置Startup 配置ConfigureSer ...
- 字段类型(uniqueidentifier)问题
环境:SQL 2016: 语句 select * from A where PID=JoID 上述查询语句中的Where PID=JoID条件中PID的字段类型为varchar(50)而JoID的字段 ...
- php ./configure的一些参数及意义
PHP编译参数的含义 ./configure –prefix=/usr/local/php php安装目录 –with-apxs2=/usr/local/apache/bin/apxs –with-c ...
- 高精度模板 支持各种运算 c++
绪言 自从有了高精度模板,妈妈再也不用怕我不会打高精度了! 代码 代码长度与日俱增啊~~~ #include<iostream> #include<cstring> #incl ...
- css基础-定位+网页布局案例
position:static 忽略top/bottom/left/right或者z-index position:relative 设置相对定位的元素不会脱离文档流 position:fixed 不 ...
- JavaScript的语法、数据类型、基本算数和逻辑运算操作
str.toString() 可以把字符串.数值.布尔值.对象转为字符串 String(str) 任何数值强制转换为字符串类型 <!DOCTYPE html> <html lang= ...
- CSS3结构类选择器补充
:empty 没有子元素(包括文本节点)的元素 :not 否定选择器 <!DOCTYPE html> <html lang="en" manifest=&quo ...
- MySQL概述及入门(一)
MySql概述及入门(一) 什么是MySQL? MySQL是当今主流的关系型数据库管理系统(记录是有行有列的数据库) , 可以与Oracle 和SQL Server 竞争 , 是最好RDBMS( ...