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 用虚函数实现多态 在理解了面向对象的继承机制之后,我们知道了在大多数情况下派生类是基类的“一种”,就像“学生”是“人”类中的一种一样.既然“学生”是“人”的一种,那么在使用“人”这个概念 ...
随机推荐
- include 和require 区别
include和require的区别 1.include() 包含文件 2.include_once(filename)如果已经包含,则不再执行include_once 3.requirerequi ...
- 到2029年MRAM收入将增长170倍
一份新市场报告预计,从2018年到2029年,独立MRAM和STT-MRAM的收入将增长170倍,达到近40亿美元的收入.下一代内存技术的增长将主要由取代效率较低的内存技术(例如NOR闪存和SRAM) ...
- Spring Mvc Http 400 Bad Request问题排查
如果遇到了Spring MVC报错400,而且没有返回任何信息的情况下该如何排查问题? 问题描述 一直都没毛病的接口,今天测试的时候突然报错400 Bad Request,而且Response没有返回 ...
- docker - 如何清理硬盘中无关占用
背景 在使用docker进行容器化管理后会发现本次硬盘文件占用量在不断上升,并且即使是删除掉容器或者镜像也并不能释放掉对应的硬盘空间.本文将提供对应的docker命令用于真正释放掉该部分应被删除释放的 ...
- Swift Playgrounds for mac基础知识介绍
Swift Playgrounds是一款适用于iPad和Mac的革命性应用程序,它使Swift学习变得互动而有趣.它不需要编码知识,因此非常适合刚开始的学生.使用Swift解决难题,以掌握基本知识.S ...
- wordpress<=4.6版本任意命令执行漏洞
漏洞简述 当WordPress 使用 PHPMailer 组件向用户发送邮件.攻击者在找回密码时会使用PHPmailer发送重置密码的邮件,利用substr(字符串截取函数).$run(系统调用函数) ...
- Navicat Premium15安装与激活(破解)
Navicat premium是一款数据库管理工具,是一个可多重连线资料库的管理工具,它可以让你以单一程式同时连线到 MySQL.SQLite.Oracle 及 PostgreSQL 资料库,让管理不 ...
- PHP0004:PHP基础3
php写在 标签里
- 二、JVM之体系结构
一.JVM的架构模型 Java编译器输入的指令流基本上是一种基于栈的指令集架构,另外一种指令集架构则是基于寄存器的指令集架构. 具体来说这两种架构之间的区别: 基于栈式架构的特点 设计和实现更简单,适 ...
- HTML连载65-过渡模块的基本使用
一.过渡模块的基本使用 1.*:hover;这个伪类选择器除了可以用在a标签上,还可以用在其他任何标签上. 2.过渡三要素: (1)必须要有属性发生变化:(2)必须告诉系统哪个属性需要执行过渡效果:( ...