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.虚函数 原因:通过指针调用成员函数时,只能访问到基类的同名成员函数.在同名覆盖现象中,通过某个类的对象(指针及引用)调用同名函数,编译器会将该调用静态联编到该类的同名函数,也就是说,通过基类对象指 ...
随机推荐
- python web开发——django学习(二)第一个django网站运行成功
1.写message_form.html <!DOCTYPE html> <html lang="en"> <head> <meta ch ...
- VS 2015main函数带参数的调试
最近学习pcl,学习C++,今天让main的参数接收数据,想起没用过这样的,不知道怎么在vs里面调试 因此找了下方法,并记录下来 代码 #include<iostream> int mai ...
- PCL
PCL(PointCloudLibrary)——是一个的模块化的现代C++模板库. 其基于以下第三方库:Boost.Eigen.FLANN.VTK.CUDA.OpenNI.Qhull,实现点云相关的获 ...
- swoole实现多人游戏的要点
swoole实现多人游戏的要点 比方说多人在线 要满足以下两点 1 所有玩家的数据要实时更新到服务器数据库数据 2 写个定时器返回数据
- 七、spring生命周期之初始化和销毁方法
一.通过@Bean指定初始化和销毁方法 在以往的xml中,我们是这样配置的 <bean id="exampleInitBean" class="examples.E ...
- 细数那些Java程序员最容易犯那些错
java作为最受欢迎程度榜榜首语言,自然是广大开发者使用最多的语言.正因为有如此广泛的使用性,java开发中发生异常也比比皆是,接下来我们就来看看那些java开发中最容易出现的那些错误. 1.重复造轮 ...
- XML解析详解|乐字节
大家好,乐字节的小乐又来了,Java技术分享哪里少的了小乐!上次我们说了可扩展标记语言XML之二:XML语言格式规范.文档组成,本文将介绍重点——XML解析. 基本的解析方式有两种:一种叫 SAX ...
- Java学习关注
1.不去上课: 内部类的继承: https://blog.csdn.net/ruidianbaihuo/article/details/102092256 2.Matrix海 子 http://www ...
- WIP*更新生产批详细信息行产品配料
DECLARE l_batch_header_rec gme_batch_header%ROWTYPE; l_material_detail_rec gme_material_details%ROWT ...
- liunx 定时任务执行java程序配置流程
java jar包使用build fat jar进行打包 ------------------liunx任务创建--------------------------- 1.查看现有任务计划: cron ...