C++ 多态和虚函数

虚函数实现多态

#include <iostream>
using namespace std;//基类People
class People{
public:virtual void display();  //声明为虚函数
};
void People::display(){cout<<"无业游民。"<<endl;
}//派生类Teacher
class Teacher: public People{
public:virtual void display();  //声明为虚函数
};
void Teacher::display(){cout<<"是一名教师"<<endl;
}int main(){People *p = new People();p -> display();p = new Teacher();p -> display();return 0;
}

结果:

无业游民。
是一名教师
 

有了虚函数,基类指针指向基类对象时就使用基类的成员(包括成员函数和成员变量),指向派生类对象时就使用派生类的成员。换句话说,基类指针可以按照基类的方式来做事,也可以按照派生类的方式来做事,它有多种形态,或者说有多种表现方式,我们将这种现象称为多态(Polymorphism)

上面的代码中,同样是p->display();这条语句,当 p 指向不同的对象时,它执行的操作是不一样的。同一条语句可以执行不同的操作,看起来有不同表现方式,这就是多态。

多态是面向对象编程的主要特征之一,C++中虚函数的唯一用处就是构成多态。

int main(){People p();Teacher t();People &rp = p;People &rt = t;rp.display();rt.display();return 0;
}

引用同样可以多态

注意事项

1) 只需要在虚函数的声明处加上 virtual 关键字,函数定义处可以加也可以不加。

2) 为了方便,你可以只将基类中的函数声明为虚函数,这样所有派生类中具有遮蔽关系的同名函数都将自动成为虚函数。

3) 当在基类中定义了虚函数时,如果派生类没有定义新的函数来遮蔽此函数,那么将使用基类的虚函数。

4) 只有派生类的虚函数覆盖基类的虚函数(函数原型相同)才能构成多态(通过基类指针访问派生类函数)。例如基类虚函数的原型为virtual void func();,派生类虚函数的原型为virtual void func(int);,那么当基类指针 p 指向派生类对象时,语句p -> func(100);将会出错,而语句p -> func();将调用基类的函数。

5) 构造函数不能是虚函数。对于基类的构造函数,它仅仅是在派生类构造函数中被调用,这种机制不同于继承。也就是说,派生类不继承基类的构造函数,将构造函数声明为虚函数没有什么意义。

6) 析构函数可以声明为虚函数,而且有时候必须要声明为虚函数,这点我们将在下节中讲解。

纯虚函数

在C++中,可以将虚函数声明为纯虚函数,语法格式为:

virtual 返回值类型 函数名 (函数参数) = 0;

纯虚函数没有函数体,只有函数声明,在虚函数声明的结尾加上=0,表明此函数为纯虚函数。

最后的=0并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数”。

包含纯虚函数的类称为抽象类(Abstract Class)。之所以说它抽象,是因为它无法实例化,也就是无法创建对象。原因很明显,纯虚函数没有函数体,不是完整的函数,无法调用,也无法为其分配内存空间。

抽象类通常是作为基类,让派生类去实现纯虚函数。派生类必须实现纯虚函数才能被实例化。

Line 是一个抽象类,也是最顶层的基类,在 Line 类中定义了两个纯虚函数 area() 和 volume()。

//线
class Line{
public:Line(float len);virtual float area() = 0;virtual float volume() = 0;
protected:float m_len;
};
Line::Line(float len): m_len(len){ }

在 Rec 类中,实现了 area() 函数;所谓实现,就是定义了纯虚函数的函数体。但这时 Rec 仍不能被实例化,因为它没有实现继承来的 volume() 函数,volume() 仍然是纯虚函数,所以 Rec 也仍然是抽象类。 

//矩形
class Rec: public Line{
public:Rec(float len, float width);float area();
protected:float m_width;
};
Rec::Rec(float len, float width): Line(len), m_width(width){ }
float Rec::area(){ return m_len * m_width; }

直到 Cuboid 类,才实现了 volume() 函数,才是一个完整的类,才可以被实例化。 

//长方体
class Cuboid: public Rec{
public:Cuboid(float len, float width, float height);float area();float volume();
protected:float m_height;
};
Cuboid::Cuboid(float len, float width, float height): Rec(len, width), m_height(height){ }
float Cuboid::area(){ return 2 * ( m_len*m_width + m_len*m_height + m_width*m_height); }
float Cuboid::volume(){ return m_len * m_width * m_height; }

它们的继承关系为:Line --> Rec --> Cuboid
可以发现,Line 类表示“线”,没有面积和体积,但它仍然定义了 area() 和 volume() 两个纯虚函数。这样的用意很明显:Line 类不需要被实例化,但是它为派生类提供了“约束条件”,派生类必须要实现这两个函数,完成计算面积和体积的功能。
在实际开发中,你可以定义一个抽象基类,只完成部分功能,未完成的功能交给派生类去实现(谁派生谁实现)。这部分未完成的功能,往往是基类不需要的,或者在基类中无法实现的。虽然抽象基类没有完成,但是却强制要求派生类完成,这就是抽象基类的“霸王条款”。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/444059.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图的基本概念【数据结构】

序言 1对1的线性结构&#xff0c;一对多的树二叉树以及森林&#xff0c;第3种就是多对多的结构&#xff0c;也就是我们所要讲到的图的结构&#xff0c;图形结构是数据结构当中最复杂的一种结构&#xff0c;图形结构的特点就是在这个图当中任意两点之间都会有关系&#xff0c;这…

go语言一天入门(上)

第一个go程序 package mainimport "fmt"func main() {/* 这是我的第一个简单的程序 */fmt.Println("Hello, World!") } 第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包&#xff0c;如&#xff1a;package m…

Jquery 全选,反选

<script src"../js/jquery-1.6.2.min.js" type"text/javascript"></script><script language"javascript" type"text/javascript">$(function(){$("#selectAll").click(function(){//全选$("#playLi…

go语言一天入门(下)

结构体 和c一样 package mainimport "fmt"type Books struct {title stringauthor stringsubject stringbook_id int }func main() {// 创建一个新的结构体fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407}…

图的遍历算法【数据结构F】

图的遍历算法有哪两种&#xff1f; 深度优先调度算法---------将图结构看成是树形结构&#xff0c;树形结构的子图直接是没有交叉的&#xff0c;但是对于图结构的树形结构之间是有交叉的&#xff0c;类比于树形结构的二叉树&#xff0c;左指数和右指数都会相应的经历三次&#…

go语言快速刷《程序员面试金典》(1)

实现一个算法&#xff0c;确定一个字符串 s 的所有字符是否全都不同。 一个数组统计是否有 func isUnique(astr string) bool {var arr[26] int;for _,ch:range astr{num:ch-aif(arr[num]1){return false}arr[num]}return true } 给定两个字符串 s1 和 s2&#xff0c;请编写一…

最小生成树【数据结构】

前提 【1】网的最小生成树&#xff0c;涉及到生成树了那么就会有最小的权值在里面了 【2】对于一个图来说生成树是由多个的&#xff0c;并不是唯一的 【3】&#xff1a;广度优先算法的遍历是可以得到生成树的&#xff0c;深度优先算法也是可以得到生成树的 任意的一个联通网&am…

go语言快速刷《程序员面试金典》(2)

字符串轮转。给定两个字符串s1和s2&#xff0c;请编写代码检查s2是否为s1旋转而成&#xff08;比如&#xff0c;waterbottle是erbottlewat旋转后的字符串&#xff09;。 示例1 输入&#xff1a;s1 "waterbottle", s2 "erbottlewat" 输出&#xff1a;T…

广义表的基本概念【数据结构】

实名广义表与匿名广义表的区别&#xff1a;对于匿名的广义表的表示方法我们认为一对括号就是一个广义表&#xff0c;里面的数据可以是广义表也可以是 原子&#xff0c;对于有名字的广义表&#xff0c;也就是大写的字母我们可以直接认为大写的就是广义表的表示方法小练习----广义…

go语言快速刷《程序员面试金典》(3)

编写程序以 x 为基准分割链表&#xff0c;使得所有小于 x 的节点排在大于或等于 x 的节点之前。如果链表中包含 x&#xff0c;x 只需出现在小于 x 的元素之后(如下所示)。分割元素 x 只需处于“右半部分”即可&#xff0c;其不需要被置于左右两部分之间。 示例: 输入: head …

树和二叉树【数据结构】

基本概念 ADT的定义 基本操作 对比树形结构和线性结构 基本术语以及注意事项-不能错误简单的我以为 二叉树是度数小于等于2的树&#xff0c;而不是度为2的树&#xff0c;一定要记住这个概念 小知识&#xff1a;二进制转换成为十进制的方法名称叫做位权求和法&#xff0c;用到…

leetcode557. 反转字符串中的单词 III python,处理字符串的神!

给定一个字符串&#xff0c;你需要反转字符串中每个单词的字符顺序&#xff0c;同时仍保留空格和单词的初始顺序。 示例 1: 输入: "Lets take LeetCode contest" 输出: "steL ekat edoCteeL tsetnoc" 注意&#xff1a;在字符串中&#xff0c;每个单词由…

数据库2.1.1mysql的特点

在mysql5.1当中&#xff0c;mysqlab公司引入了新的插件式存储引擎体系结构&#xff0c;也许将存储引擎加载到正在运行的mysql服务器当中&#xff0c;使用mysql插件是存储引擎体系结构允许数据库用户为特定的应用需求选择专门的存储引擎&#xff0c;完全不需要管理任何特殊的应用…

leetcode369. 给单链表加一

用一个 非空 单链表来表示一个非负整数&#xff0c;然后将这个整数加一。 你可以假设这个整数除了 0 本身&#xff0c;没有任何前导的 0。 这个整数的各个数位按照 高位在链表头部、低位在链表尾部 的顺序排列。 示例: 输入: [1,2,3] 输出: [1,2,4] 思路&#xff1a; hel…

MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇

一 MyISAM 1.1 MyISAM简介 MyISAM是MySQL的默认数据库引擎&#xff08;5.5版之前&#xff09;&#xff0c;由早期的 ISAM &#xff08;Indexed Sequential Access Method&#xff1a;有索引的顺序访问方法&#xff09;所改良。虽然性能极佳&#xff0c;而且提供了大量的特性&a…

leetcode193. 有效电话号码 正则了解一下

给定一个包含电话号码列表&#xff08;一行一个电话号码&#xff09;的文本文件 file.txt&#xff0c;写一个 bash 脚本输出所有有效的电话号码。 你可以假设一个有效的电话号码必须满足以下两种格式&#xff1a; (xxx) xxx-xxxx 或 xxx-xxx-xxxx。&#xff08;x 表示一个数字…

leetcode258. 各位相加

给定一个非负整数 num&#xff0c;反复将各个位上的数字相加&#xff0c;直到结果为一位数。 示例: 输入: 38 输出: 2 解释: 各位相加的过程为&#xff1a;3 8 11, 1 1 2。 由于 2 是一位数&#xff0c;所以返回 2。 进阶: 你可以不使用循环或者递归&#xff0c;且在 O(…

leetcode412. Fizz Buzz

写一个程序&#xff0c;输出从 1 到 n 数字的字符串表示。 1. 如果 n 是3的倍数&#xff0c;输出“Fizz”&#xff1b; 2. 如果 n 是5的倍数&#xff0c;输出“Buzz”&#xff1b; 3.如果 n 同时是3和5的倍数&#xff0c;输出 “FizzBuzz”。 示例&#xff1a; n 15, 返…

leetcode359. 日志速率限制器

请你设计一个日志系统&#xff0c;可以流式接收日志以及它的时间戳。 该日志会被打印出来&#xff0c;需要满足一个条件&#xff1a;当且仅当日志内容 在过去的 10 秒钟内没有被打印过。 给你一条日志的内容和它的时间戳&#xff08;粒度为秒级&#xff09;&#xff0c;如果这…

怎样提高WebService性能大数据量网络传输处理(转)

1. 直接返回DataSet对象 特点&#xff1a;通常组件化的处理机制&#xff0c;不加任何修饰及 处理&#xff1b; 优点&#xff1a;代码精减、易于处理&#xff0c;小数据量处理较快&#xff1b; 缺点&#xff1a;大数据量的传递处理慢&#xff0c;消耗网络资源&#xff1b; 建议&…