C++之普通成员函数、虚函数以及纯虚函数的区别与用法要点

2023-11-16

C++之普通成员函数、虚函数以及纯虚函数的区别与用法要点

作者:luoweifu 字体:[增加 减小] 类型:转载 时间:2015-07-21 我要评论

本篇文章主要介绍了C++中的普通成员函数、虚函数以及纯虚函数,非常的详细,有需要的朋友可以参考下

普通成员函数是静态编译的,没有运行时多态,只会根据指针或引用的“字面值”类对象,调用自己的普通函数;虚函数为了重载和多态的需要,在基类中定义的,即便定义为空;纯虚函数是在基类中声明的虚函数,它可以再基类中有定义,且派生类必须定义自己的实现方法。

假设我们有三个类Person、Teacher、Student它们之间的关系如下:


这里写图片描述
类的关系图 

普通成员函数

【Demo1】 
根据这个类图,我们有下面的代码实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#ifndef __OBJEDT_H__
#define __OBJEDT_H__
 
#include <string>
#include <iostream>
 
class Person
{
public :
  Person( const string& name, int age) : m_name(name), m_age(age)
  {
  }
 
  void ShowInfo()
  {
  cout << "姓名:" << m_name << endl;
  cout << "年龄:" << m_age << endl;
  }
 
protected :
  string m_name; //姓名
  int m_age; //年龄
};
 
 
class Teacher : public Person
{
public :
  Teacher( const string& name, int age, const string& title)
  : Person(name, age), m_title(title)
  {
  }
 
  void ShowInfo()
  {
  cout << "姓名:" << m_name << endl;
  cout << "年龄:" << m_age << endl;
  cout << "职称:" << m_title << endl;
  }
 
private :
  string m_title; //职称
};
 
class Student : public Person
{
public :
  Student( const string& name, int age, int studyId)
  : Person(name, age), m_studyId(studyId)
  {
  }
 
  void ShowInfo()
  {
  cout << "姓名:" << m_name << endl;
  cout << "年龄:" << m_age << endl;
  cout << "学号:" << m_studyId << endl;
  }
 
private :
  int m_studyId;  //学号
};
 
#endif //__OBJEDT_H__

测试代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void test()
{
  Person* pPerson = new Person( "张三" , 22);
  Teacher* pTeacher = new Teacher( "李四" , 35, "副教授" );
  Student* pStudent = new Student( "王五" , 18, 20151653);
  pPerson->ShowInfo();
  cout << endl;
  pTeacher->ShowInfo();
  cout << endl;
  pStudent->ShowInfo();
  cout << endl;
  delete pPerson;
  delete pTeacher;
  delete pStudent;
}

结果:

姓名:张三 
年龄:22

姓名:李四 
年龄:35 
职称:副教授

姓名:王五 
年龄:18 
学号:20151653
说明: 
这里的ShowInfo就是一个普通的函数。pPerson、pTeacher和pStudent三个对象调用ShowInfo分别展示自己的信息。 
我们知道:父类的指针是可以指向子类的对象的。我们把上面的测试代码稍微改一下: 
【Demo2】

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void test()
{
  Person* pPerson = new Person( "张三" , 22);
  Person* pTeacher = new Teacher( "李四" , 35, "副教授" );
  Person* pStudent = new Student( "王五" , 18, 20151653);
  pPerson->ShowInfo();
  cout << endl;
  pTeacher->ShowInfo();
  cout << endl;
  pStudent->ShowInfo();
  cout << endl;
  delete pPerson;
  delete pTeacher;
  delete pStudent;
}

结果:

姓名:张三 
年龄:22

姓名:李四 
年龄:35

姓名:王五 
年龄:18
这时,pTeacher和pStudent只输出了姓名和年龄,并没有输出子类所具有的特性(职称和学号)。这应该不是你期望的结果,你可能期望pTeacher和pStudent输出老师和学生的完整信息,这时就需要用虚函数。

虚函数

我们把Person中的ShowInfo成员改成虚函数(在前面加上virtual),代码如下: 
【Demo3】

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person
{
public :
  Person( const string& name, int age) : m_name(name), m_age(age)
  {
  }
 
  virtual void ShowInfo()
  {
  cout << "姓名:" << m_name << endl;
  cout << "年龄:" << m_age << endl;
  }
 
protected :
  string m_name; //姓名
  int m_age; //年龄
};

在执行上面【Demo2】中的测试代码,得到我们想到的结果:

姓名:张三 
年龄:22

姓名:李四 
年龄:35 
职称:副教授

姓名:王五 
年龄:18 
学号:20151653
虚函数用法要点:

  1. 虚函数的声明方式:virtual RETURN_TYPE functionName(ARGS 参数列表);
  2. 虚函数作用:现实C++中的多态,进行动态绑定(父类指针可指向子类的对象),直到运行时才知道要调用哪个版本(哪个类定义)的函数;
  3. 我们必要对虚函数进行定义;
  4. 一旦父类的成员函数声明virtual,其子类的函数不管有没有声明为virtual,都是虚函数;
  5. 如果虚函数使用默认实参,父类和子类定义的默认实参最好一致。

【Demo4】:针对第4点说明:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Person
{
public :
  Person( const string& name, int age) : m_name(name), m_age(age)
  {
  }
 
  virtual void ShowInfo()
  {
  cout << "姓名:" << m_name << endl;
  cout << "年龄:" << m_age << endl;
  }
 
  string GetName(); //正确,普通函数如果不被使用,可以只有声明没有定义
  virtual int GetAge(); //错误,虚函数必须要有定义,即使是一个空实现,因为编译器无法确定会使用哪个函数
 
 
protected :
  string m_name; //姓名
  int m_age; //年龄
};

【Demo5】:针对第5点进行说明: 
设计我们的类如下定义。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Person
{
public :
  virtual void SetAge( int age = 0)
  {
  m_age = age;
  }
  //... 省略
};
 
 
class Teacher : public Person
{
public :
  virtual void SetAge( int age = 1)
  {
  m_age = age;
  }
 
  //... 省略
};
 
class Student : public Person
{
public :
  virtual void SetAge( int age = 2)
  {
  m_age = age;
  }
 
  //... 省略
};

测试1:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void test()
{
  Person* pPerson = new Person( "张三" , 22);
  Teacher* pTeacher = new Teacher( "李四" , 35, "副教授" );
  Student* pStudent = new Student( "王五" , 18, 20151653);
  pPerson->SetAge();
  pTeacher->SetAge();
  pStudent->SetAge();
 
  pPerson->ShowInfo();
  cout << endl;
  pTeacher->ShowInfo();
  cout << endl;
  pStudent->ShowInfo();
  cout << endl;
  delete pPerson;
  delete pTeacher;
  delete pStudent;
}

结果:

姓名:张三 
年龄:0

姓名:李四 
年龄:1 
职称:副教授

姓名:王五 
年龄:2 
学号:20151653
测试2:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void test()
{
  Person* pPerson = new Person( "张三" , 22);
  Person* pTeacher = new Teacher( "李四" , 35, "副教授" );
  Person* pStudent = new Student( "王五" , 18, 20151653);
  pPerson->SetAge();
  pTeacher->SetAge();
  pStudent->SetAge();
 
  pPerson->ShowInfo();
  cout << endl;
  pTeacher->ShowInfo();
  cout << endl;
  pStudent->ShowInfo();
  cout << endl;
  delete pPerson;
  delete pTeacher;
  delete pStudent;
}

结果:

姓名:张三 
年龄:0

姓名:李四 
年龄:0 
职称:副教授

姓名:王五 
年龄:0 
学号:20151653
纯虚函数

在上面的例子中,我们假设所有的人都要工作,但不同的人工作的方式不同。于是我们就要强制要求继承自Person的子类都要有工作的方法,这就需要纯虚函数。定义如下: 
【Demo6】

?
1
2
3
4
5
6
7
class Person
{
public :
  //... 省略
  virtual void DoWork() = 0;
  //... 省略
};

但此时我们编译

?
1
Person* pPerson = new Person( "张三" , 22);


这句话时会报错:error C2259: ‘Person' : cannot instantiate abstract class 
这是因为我们并没有为Person实现DoWork方法,而包含纯虚函数的类是一个抽象的类,抽象类不能被实例化。

于是我们在子类中对它实现如下: 
【Demo7】

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Teacher : public Person
{
public :
  //... 省略
  virtual void DoWork()
  {
  cout << "教书..." << endl;
  }
  //... 省略
};
 
class Student : public Person
{
public :
  //... 省略
  virtual void DoWork()
  {
  cout << "学习..." << endl;
  }
  //... 省略
};

没用DoWork方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
void test()
{
  Person* pTeacher = new Teacher( "李四" , 35, "副教授" );
  Person* pStudent = new Student( "王五" , 18, 20151653);
 
  pTeacher->DoWork();
  cout << endl;
  pStudent->DoWork();
  cout << endl;
 
  delete pTeacher;
  delete pStudent;
}

   结果:

    教书…

    学习…
纯虚函数用法要点:

  1. 纯虚函数的声明方式:virtual RETURN_TYPE functionName(ARGS 参数列表) = 0;
  2. 含有纯虚函数的类是一个抽象的类,抽象类不能被实例化。
  3. 包含纯虚函数的抽象类常用来当作对外的接口,说明这个类有什么功能,而没有具体的实现,基体的实现交由子类完成。

通过以上对普通成员函数、虚函数以及纯虚函数的介绍,希望可以对大家有所帮助。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++之普通成员函数、虚函数以及纯虚函数的区别与用法要点 的相关文章

随机推荐

  • 单目测距(yolo目标检测+标定+测距代码)

    单目测距 目标检测 标定 测距 实时感知本车周围物体的距离对高级驾驶辅助系统具有重要意义 当判定物体与本车距离小于安全距离时便采取主动刹车等安全辅助功能 这将进一步提升汽车的安全性能并减少碰撞的发生 上一章本文完成了目标检测任务 接下来需要
  • 一枚芯片的成本是多少?(2)芯片硬件成本

    芯片硬件成本 计算封装和测试的成本这个没有具体的公式 只是测试的价格大致和针脚数的二次方成正比 封装的成本大致和针脚乘功耗的三次方成正比 如果CPU X采用40nm低功耗工艺的自主芯片 其测试成本约为2美元 封装成本约为6美元 因40nm低
  • nodejs调用mongodb!!!

    Nodejs调用MongoDB 要在 MongoDB 中创建一个数据库 首先我们需要创建一个 MongoClient 对象 然后配置好指定的 URL 和 端口号 如果数据库不存在 MongoDB 将创建数据库并建立连接 导入MongoDB包
  • 华为VS谷歌:万物互联,谁主沉浮?

    一 一周两套操作系统发布 6月2日 华为通过直播形式举行了鸿蒙HarmonyOS 2及华为全场景新品发布会 关于该发布会的详细内容老猿在 鸿蒙最新功能及承载设备详解 HarmonyOS 2及华为全场景新品发布会全纪录 进行了详细介绍 在此不
  • 【科普】一文读懂以太网PHY芯片

    物理层器件PHY Physical Layer Interface Devices 是将各网元连接到物理介质上的关键部件 负责完成互连参考模型 OSI 第1层中的功能 即为链路层实体之间进行bit传输提供物理连接所需的机械 电气 光电转换和
  • 嵌入式中锁机制杂谈

    在之前的文章中有提到操作系统中锁机制一般依赖于硬件CPU提供的原子数据操作指令 如SWP TEST AND SET等原子原语实现 基于此 才能真正保证锁机制的有效实现 通过上面原子操作 我们比较容易实现所谓的自旋操作 原子性的原地循环判断条
  • np.random.choice用法

    np random choice a size replace p 其作用是按要求生成一个一维数组 a是生成一维数组的来源 可以是int类型 可以是数组 也可以是list size 为从a中抽取的个数 即生成数组的维度 replace 表示
  • 《数据库系统》课程之实验七 通过ODBC/JDBC转移异构数据库中数据

    注 查看全文请关注作者 或点击前往 数据库系统 课程之实验七 通过ODBC JDBC转移异构数据库中数据 数据库系统 课程之实验七 通过ODBC JDBC转移异构数据库中数据 1 实验目的 学会配置ODBC JDBC数据源 熟悉使用ODBC
  • QueryWrapper方法解释

    继承自 AbstractWrapper 自身的内部属性 entity 也用于生成 where 条件 及 LambdaQueryWrapper 可以通过 new QueryWrapper lambda 方法获取 queryWrapper lt
  • PyTorch实战——搭建PyTorch神经网络进行气温预测

    如果觉得我的分享对您的学习有帮助 可以点赞关注哈 谢谢哈 目录 编辑 一 理论部分 二 代码实战 1 导入模块 1 matplotlib inline 2 warnings filterwarnings ignore 2 读入数据 3 展示
  • 三极管电路共集、共基、共射的区别

    共集 共基 共射指的是电路 是三极管电路的连接状态而不是三极管 所谓 共 就是输入 输出回路共有的部分 其判断是在交流等效电路下进行的 共集电极电路 三极管的集电极接地 集电极是输入与输出的公共极 共基极电路 三极管的基极接地 基极是输入与
  • 安装ubuntu系统时给/home分配空间太小,导致训练模型时数据集无法存放,所以给/home增大100GGB的存储空间

    1 从Windows系统中分配出100GB的存储空间 2 制作gparted的U盘启动项 3 插入U盘 进入bios界面 选择U盘启动项 4 进入gparted软件界面进行存储空间的转移重新分配 5 Exit退出 重新进入linux系统 参
  • 微信小程序 WebSocket 端口号配置

    https blog liuguofeng com p 4630 服务端开启 WebSocket 使用 WorkerMan phpSocket io 开启的端口为 2120 访问为 ws wanaioa unetu net 2120 由于微
  • 基于 java Swing 客户端 和 Spring Boot/Spring Cloud & Alibaba 后台管理系统

    基于 java Swing 客户端 和 Spring Boot Spring Cloud Alibaba 后台管理系统 基于 java Swing 客户端 和 Spring Boot Spring Cloud Alibaba 后台管理系统
  • 【Java JDK的使用方法】

    Java JDK的使用方法 第一步 同时按住窗口键和R键 在弹出的运行框中输入cmd打开编译框 第二步 输入cd 空格 地址 可以查看桌面文本文档的属性 找到桌面地址 第三步 notepad 空格 文件名 java 新建java文件 第四步
  • 何为UNP技术?

    为了解决移动视频监控系统中的这种穿NAT型 宇视科技特意提出了UNP UniversalNetwork Passport 万能网络护照 技术 目前 针对监控系统穿越NAT设备 防火墙和安全网闸时 基本上都是使用引流方案 内部服务器 双网口方
  • C++ 调用tensorflow

    安装protobuf 3 6 安装依赖软件 sudo apt get install autoconf automake libtool curl make g unzip 克隆版本为3 6 0的protobuf源码 git clone b
  • 8.翻转子串

    题目描述 假定我们都知道非常高效的算法来检查一个单词是否为其他字符串的子串 请将这个算法编写成一个函数 给定两个字符串s1和s2 请编写代码检查s2是否为s1旋转而成 要求只能调用一次检查子串的函数 给定两个字符串s1 s2 请返回bool
  • 1-如何安装ROS

    如何安装ROS 大家好 我是如何 今天尝试在Ubantu下安装ROS Robot Operating System 测试环境 虚拟机VMware Ubantu20 04 准备步骤 添加ROS软件源 sudo sh c echo deb ht
  • C++之普通成员函数、虚函数以及纯虚函数的区别与用法要点

    C 之普通成员函数 虚函数以及纯虚函数的区别与用法要点 作者 luoweifu 字体 增加 减小 类型 转载 时间 2015 07 21 我要评论 本篇文章主要介绍了C 中的普通成员函数 虚函数以及纯虚函数 非常的详细 有需要的朋友可以参考