C++(四十一) — 多态、虚函数、虚析构函数、纯虚函数
1、多态
面向对象程序设计中,多态性表现为:
(1)重载多态:函数重载、运算符重载;
(2)运行多态:通过基类的指针(或引用)调用不同派生类的同名函数,表现出不同的行为;
(3)模板多态:参数多态,通过一个模板得到不同的函数或不同的类,具有不同的特性和不同的行为;
2、同名覆盖与重载
(1)override(同名覆盖)
在类的继承中才会出现,多个函数的原型是相同的。
(2)overload(重载)
在同一作用域范围内,由参数个数或类型不同的多个同名函数构成。
3、虚函数
原因:通过指针调用成员函数时,只能访问到基类的同名成员函数。在同名覆盖现象中,通过某个类的对象(指针及引用)调用同名函数,编译器会将该调用静态联编到该类的同名函数,也就是说,通过基类对象指针是无法访问派生类的同名函数的,即使这个指针是用派生类对象来初始化的。
虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。
虚函数给基类指针访问派生类同名函数的一个机会;
指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。
可以说,基类声明的虚函数,在派生类中也是虚函数,即使不再使用virtual关键字。
虚函数实现多态的原理:
- 当类中声明虚函数时,编译器会在类中生成一个虚函数表,用于存储类成员函数的指针,由编译器自动生成和维护;
- 存在虚函数时,每个对象都有一个指向虚函数表的指针(对于派生类的虚函数表,先放父类,后方子类,函数覆盖时就用子类的同名函数代替父类的);
- 编译器确定是否为虚函数,如是则根据对象的指针,找到所指虚函数表查找函数并调用。
动态联编:一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为“虚”函数。
虚函数只能借助于指针或者引用来达到多态的效果。但会为程序引入较大的开销,实际中应尽量避免。
注意:
虚函数在基类中声明,构造函数、静态成员函数不可以为虚函数,但析构函数可以。
使用角度:虚函数通过父类指针调用子类的成员函数,而构造函数在创建时自动调用,无需父类指针;
存储角度:虚函数对应一个指向虚函数表的指针,虚函数表通过构造函数初始化,若构造函数为虚,则需要通过虚函数表来找到虚构造函数的入口地址,而此时无虚函数表,所以构造函数不能为虚函数。
4、虚析构函数
只有虚析构函数,没有虚构造函数。
创建派生类对象时,调用基类构造->派生类构造->派生类析构->基类析构。
如果用new运算符动态创建派生类对象,并以此对象地址初始化基类指针,构造没问题,但用delete运算符删除派生类对象时,由于指针是指向基类的,通过静态联编,调用基类析构函数,不调用派生类析构函数,使得派生类无法执行某些清理工作,例如:派生类中申请的内存没机会还给系统。
虚析构函数:基类设置虚析构函数,派生类都是。此时使用基类对象指针销毁派生类对象时,会通过动态联编调用派生类析构函数,完成派生类的清理工作。
5、纯虚函数
如下声明表示一个函数为纯虚函数:
class A
{
public:
virtual void foo()=0; // =0 标志一个虚函数为纯虚函数,没有函数体,不可实例化,不可被调用。
};
一个函数声明为纯虚后,纯虚函数的意思是:我是一个抽象类!不要把我实例化!纯虚函数用来规范派生类的行为,实际上就是所谓的“接口”。它告诉使用者,我的派生类都会有这个函数。
纯虚函数的引入,是出于两个目的:
1、为了安全,因为避免任何需要明确但是因为不小心而导致的未知的结果,提醒子类去做应做的实现。实际上是限制了派生类的功能,规范接口,把实现留给子类。
2、为了效率,不是程序执行的效率,而是为了编码的效率。
6、运行时多态的应用实例
(1)头文件 shape.h
#pragma once //#ifdef SHAPE_H #include <iostream>
using namespace std; class Shape
{
public:
virtual double getArea() const = ;//纯虚函数
void print() const;
virtual ~Shape() {} //虚析构函数
}; class Circle :public Shape
{
public:
Circle(int xv= , int yv= , double rv= 0.0);
double getArea() const;
void print() const;
protected:
int x, y;
double r;
}; class Rectangle :public Shape
{
public:
Rectangle(int av= , int bv = );
double getArea() const;
void print() const;
protected:
int a, b;
}; //#endif // DEBUG
(2)函数定义 shape.c
#include <iostream>
using namespace std;
#include "shape.h" void Shape::print() const
{
cout << "Base class Object" << endl;
} Circle::Circle(int xv, int yv, double rv)
{
x = xv; y = yv;
r = rv;
}
double Circle::getArea() const
{
return 3.14*r*r;
}
void Circle::print() const
{
cout << "center is" << x << " " << y << endl;
} Rectangle::Rectangle(int av, int bv)
{
a = av; b = bv;
}
double Rectangle::getArea() const
{
return a*b;
}
void Rectangle::print() const
{
cout << "h is " << a << " " << b << endl;
}
3、测试文件
#include <iostream>
using namespace std;
#include "shape.h" void creat_object(Shape **ptr);
void display_area(Shape *ptr);
void delete_object(Shape *ptr); void main()
{
Shape *shape_ptr;
creat_object(&shape_ptr);
display_area(shape_ptr);
delete_object(shape_ptr);
system("pause");
} void creat_object(Shape **ptr)
{
char type;
*ptr = nullptr;
do {
cout << "创建对象:" << endl;
cin >> type;
switch (type)
{
case 'c':
{
int xx, yy; double rr;
cout << "请输入圆心及半径:";
cin >> xx >> yy >> rr;
*ptr = new Circle(xx, yy, rr);
break;
}
case 'r':
{
int aa, bb;
cout << "请输入矩形的长和宽:";
cin >> aa >> bb;
*ptr = new Rectangle(aa,bb);
break;
}
default:cout << "请重新选择\n" << endl;
}
} while (*ptr == nullptr);
} void display_area(Shape *ptr)
{
cout << ptr->getArea() << endl;
} void delete_object(Shape *ptr)
{
delete(ptr);
}
C++(四十一) — 多态、虚函数、虚析构函数、纯虚函数的更多相关文章
- 从零开始学C++之虚函数与多态(二):纯虚函数、抽象类、虚析构函数
一.纯虚函数 虚函数是实现多态性的前提 需要在基类中定义共同的接口 接口要定义为虚函数 如果基类的接口没办法实现怎么办? 如形状类Shape 解决方法 将这些接口定义为纯虚函数 在基类中不能给出有意义 ...
- C++中为什么构造函数不能是虚函数,析构函数是虚函数
一, 什么是虚函数? 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离:用形象的语 ...
- C++虚函数virtual,纯虚函数pure virtual和Java抽象函数abstract,接口interface与抽象类abstract class的比较
由于C++和Java都是面向对象的编程语言,它们的多态性就分别靠虚函数和抽象函数来实现. C++的虚函数可以在子类中重写,调用是根据实际的对象来判别的,而不是通过指针类型(普通函数的调用是根据当前指针 ...
- C++入门经典-例8.9-抽象类,纯虚函数,创建纯虚函数
1:包含有纯虚函数的类称为抽象类,一个抽象类至少具有一个纯虚函数.抽象类只能作为基类派生出的新的子类,而不能在程序中被实例化(即不能说明抽象类的对象),但是可以使用指向抽象类的指针.在程序开发过程中并 ...
- C++多态,虚函数,虚函数表,纯虚函数
1.多态性 指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作. C++支持两种多态性:编译时多态性,运行时多态性. a.编译时多态性:通过重载函数实现 ,模板(2次编译) ...
- C++多态、虚函数、纯虚函数、抽象类、虚基类
一.C++多态 C++的多态包括静态多态和动态多态.静态多态包括函数重载和泛型编程,动态多态包括虚函数.静态多态是指在编译期间就可以确定,动态多态是指在程序运行时才能确定. 二.虚函数 1.虚函数为类 ...
- C++回顾day03---<纯虚函数和抽象类以及虚析构函数,delete使用>
一:纯虚函数和抽象类 纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本 纯虚函数为各个派生类提供一个公共接口 纯虚函数的形式: virtual 类型 函数名(参数列 ...
- C++多态、虚函数、纯虚函数、抽象类
多态 同一函数调用形式(调用形式形同)可以实现不同的操作(执行路径不同),就叫多态. 两种多态: (1)静态多态:分为函数重载和运算符重载,编译时系统就能决定调用哪个函数. (2)动态多态(简称多态) ...
- C++(九)— 虚函数、纯虚函数、虚析构函数
1.虚函数 原因:通过指针调用成员函数时,只能访问到基类的同名成员函数.在同名覆盖现象中,通过某个类的对象(指针及引用)调用同名函数,编译器会将该调用静态联编到该类的同名函数,也就是说,通过基类对象指 ...
随机推荐
- 【Tools】HP/惠普v285w 量产工具
前段时间朋友说自己u盘坏了,让帮忙看看.看下图是这个u盘. 坏的问题:往里面复制东西,提示:请去掉写保护或使用另一张磁盘.但是能正常从里面读取出来数据. 无论更换电脑,还是使用网上的修改注册表等方式皆 ...
- 悬架的灵魂——K&C特性
静止便是死亡,只有运动才能敲开永生的大门 — 泰戈尔 KC特性是车辆操控稳定性的直接影响者!可以分为 K ( Kinematic) 特性和 C( Compliance) 特性: K 特性即悬 ...
- C++和C API调用
c++是C的超集,不可避免的要兼容C的特性,C++在C基础山的拓展部分叫做C with class,同时C++有自己特有的属性比如模板template. C并不完全是C++的子集. 那么如何在C/C+ ...
- Linux中buff/cache内存占用过高解决办法
在Linux系统中,我们经常用free命令来查看系统内存的使用状态.在一个centos7的系统上,free命令的显示内容大概是这样一个状态: 这个命令几乎是每一个使用过Linux的人必会的命令,但越是 ...
- python 之网络并发(非阻塞IO模型)
实现gevent模块 服务端: from socket import * import time s = socket() s.bind(('127.0.0.1',8080)) s.listen(5) ...
- spring session cpu占用过高
集成spring session很简单,只需几行代码即可. @Configuration @EnableRedisHttpSession public class SessionConfig { ...
- array_walk、array_map、array_filter 的用法
array_walk.array_map.array_filter 和 foreach 都有循环对数组元素进行处理的功能. 一.array_walk 用法 1.循环数组,回调处理(并不修改数组元素的 ...
- quartz2.3.0(十)xml配置方式定义quartz定时任务
1.新增pom依赖 除了按照<quartz2.3.0系列目录——带您由浅入深全面掌握quartz2.3.0>添加依赖之外,pom.xml里新增加依赖: <dependency> ...
- Python 获取本月的最后一天
一.需求 现在有一个场景,需要每月的最后一天,发送一封邮件. 二.获取本月最后一天 有没有办法使用Python的标准库轻松确定(即一个函数调用)给定月份的最后一天? 答案是有的,使用 datetime ...
- jquery easyui datagrid 在翻页以后仍能记录被选中的行及刷新设置选中行数据
//easyUI的datagrid在复选框多选时,如何在翻页以后仍能记录被选中的行://注意datagrid中需要配置idField属性,一般为数据的主键 $.ajax({ type: 'GET', ...