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 - Django - 使用 Bootstrap 样式修改注册页
reg2 函数: from django.shortcuts import render, HttpResponse from app01 import models def reg2(request ...
- MyBatis插入记录时返回主键id的方法
有时候插入记录之后需要使用到插入记录的主键,通常是再查询一次来获取主键,但是MyBatis插入记录时可以设置成返回主键id,简化操作,方法大致有两种. 对应实体类: public class User ...
- aligin-items与aligin-content的区别
align-items 属性使用于所有的flex容器,它是用来设置每个flex元素在侧轴上的默认对齐方式 aligin-items 与align-content有相同的功能,不过不同点是它是用来让每一 ...
- [LeetCode] 362. Design Hit Counter 设计点击计数器
Design a hit counter which counts the number of hits received in the past 5 minutes. Each function a ...
- public interface ICloneable
using System.Runtime.InteropServices; namespace System{ // // 摘要: // 支持克隆,即用与现有实例相同的值创建类的新实例. [ComVi ...
- mysql杂项
取数据库某个表中的所有的字段 select COLUMN_NAME from information_schema.COLUMNS where table_name = 'your_table_nam ...
- sql 查找所有已经分配部门的员工
查找所有已经分配部门的员工的last_name和first_nameCREATE TABLE `dept_emp` (`emp_no` int(11) NOT NULL,`dept_no` char( ...
- win10 远程连接怎么设置快捷方式
在桌面空白处右键,选择新建快捷方式,然后输入命令:C:\windows\system32\mstsc.exe,点击下一步,然后输入快捷方式名称:远程连接,点击确定即可.
- CF28B pSort
题目描述 给定一个含有n个元素的数列,第i号元素开始时数值为i,元素i可以与距离为d[i]的元素进行交换.再给定一个1-n的全排列,问初始的数列可否交换成给定的样式. 输入:第一行一个整数n,第二行n ...
- 【Tools】VMware虚拟机三种网络模式详解和操作
目录 00. 目录 01. VMware虚拟机三种网络模式 02. Bridged(桥接模式) 03. NAT(地址转换模式) 04. Host-Only(仅主机模式) 00. 目录 @ 参考:htt ...