C++|多态性与虚函数(1)功能绑定|向上转换类型|虚函数

简介: C++|多态性与虚函数(1)功能绑定|向上转换类型|虚函数

什么是多态性?

概念

书上的表示是——向不同的对象发送同一个消息,不同的对象在接受时会有不同的反应,产生不同的动作。

我们在上课时,老师是这么解释的:在一个学校里,每个人都有不同的工作,当校长发出号令“开工”,每个人就开始工作(属于自己的那份工作)。

具体一点:多态性是指用一个名字定义不同的函数,这些函数执行不同但又类似的操作,从而可以使用相同的口令来调用相应函数——一个接口,多个方法。

分类

多态性可以分为——参数多态,包含多态,重载多态,强制多态。

参数多态:函数模板,类模板

由函数模板实例化的各个函数具有相同的操作,而这些函数的参数类型却各不相同

由类模板实例化的各个类具有相同的操作,而操作对象的类型各不相同

包含多态定义于不同类之间的同名成员函数,主要通过虚函数来实现。

重载多态:函数重载,运算符重载

函数重载有就是在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数、类型、类型顺序)不同。

运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用于不同类型时导致不同的行为。

强制多态:是指将一个变元的类型加以变化,以符合一个函数(或者操作)的要求

如加法运算符在进行浮点数于整形数相加时,首先进行类型强制转换。把整型数变为浮点数再相加的情况。

向上类型转换

向上类型转换就是把一个派生类对象当成基类对象来用

注意:

向上类型转换是安全的

向上类型转换可以自动完成

向上类型转换的过程会丢失子类信息

#include<iostream>
using namespace std;
 
class point
{
public:
  point(double a, double b):x(a), y(b){}
  void area(void)
  {
    cout << "point:" << 0 << endl;
  }
 
 
  double x;
  double y;
 
};
 
class rectangle:public point
{
public:
  rectangle(double a,double b,double c,double d):point(a,b),x1(c),y1(d){}
  void area(void)
  {
    cout << "rectangle:" << (x-x1)*(y-y1)<< endl;
  }
 
private:
  double x1;
  double y1;
};
 
class circle :public point
{
public:
  circle(double a,double b,double c):point(a,b),r(c){}
  void area(void)
  {
    cout << "circle:" <<r*r*3.14<< endl;
  }
private:
  double r;
};
 
 
void calcarea(point& p)
{
  p.area();
}
 
 
int main(void)
{
  point p(0, 0);
  rectangle r(0, 0, 1, 1);
  circle c(0, 0, 1);
  cout << "直接调用:" << endl;
 
  p.area();
  r.area();
  c.area();
  cout << "通过calearea函数调用:" << endl;
  calcarea(p);
  calcarea(r);
  calcarea(c);
 
  return 0;
}

再caleare函数中,接受point的对象,但也不拒绝point的派生类对象,无需类型转换就可以将rectangle和circle的对象传给calearea。这也就是向上类型转换,可以将派生类转换为基类,这也导致rectangle和circle类的接口变窄。

可以看到通过calearea函数调用时,输出的全是“point:0",显然不是我们希望可看到的,我们希望通过基类的引用直接调用到相应的派生类成员函数。

也就是当calearea函数中的对象是rectangle时调用rectangle中的area函数;

当calearea函数中的对象时circle的对象时调用cricle中的area函数。

为了解决这个问题需要了解功能的早绑定和晚绑定

功能的早绑定和晚绑定

绑定

确定操作具体对象的过程称为绑定。

绑定是指计算机程序自身彼此关联的过程,把一个标识符和一个存储地址联系在一起(把一条消息和一个对象的方法相结合的过程)。

绑定与多态的联系

按照绑定进行的阶段不同分为:功能的早绑定和功能的晚绑定

多态从实现的角度可以分为:编译时多态和运行时多态

两种绑定方法分别对应多态的两种实现方式

编译时多态(功能的早绑定)

编译过程中确定了同名操作的具体操作对象,在程序执行前期,系统就可以确定同名标识要调用那一段程序代码。

有些多态类型(重载多态,强制多态,参数多态)可以通过早绑定确定同名操作的具体操作对象

运行时多态(功能的晚绑定)

程序运行过程中动态的确定操作所针对的具体对象,在编译过程中无法解决绑定问题,等程序开始运行之后再来确定,包含多态就是通过晚绑定来确定具体操作对象的。

一般而言

编译型语言(C,PASCAL)都采用功能的早绑定,

解释型语言(LISP,Prolog)都采用晚绑定

早绑定具有函数调用速度快,效率高,但缺乏灵活性,晚绑定恰恰相反。

C++由C语言发展而来,为了保持C语言的高效性,C++仍采用编译型也就是早绑定,为了解决某些特定情况,发明了虚函数,让C++可部分采用功能的晚绑定。

C++中,编译时的多态性主要通过函数重载和运算符重载实现。运行时的多态主要通过虚函数来实现。

实现功能晚绑定——虚函数

虚函数提供了一种更为灵活的多态性机制。虚函数允许函数调用与函数体之间的联系在运行时才建立,也就是在运行时才决定同名操作的具体操作对象

虚函数的定义和作用

虚函数定义在基类定义,在成员函数声明前加上virtual,定义语法如下:

virtual 函数类型 函数名(形参表)

{

       函数体;

}

在基类中定义虚函数,

在派生类中重写虚函数,重写时必须满足同名,同参数,同返回值。

使用时必须通过指针或者引用来调用

上面的例子基类时point,修改:我们只需要将point类中的area函数前面加上virtual就可以达到想要的效果。

class point
{
public:
  point(double a, double b):x(a), y(b){}
  virtual void area(void)
  {
    cout << "point:" << 0 << endl;
  }
 
 
  double x;
  double y;
 
};

上面的代码是通过引用调用的,下面我们用指针来调用 ,也就是形参为指针,实参传地址。具体代码如下:

void calcarea(point* p)
{
  p->area();
}
 
 
int main(void)
{
  point p(0, 0);
  rectangle r(0, 0, 1, 1);
  circle c(0, 0, 1);
  cout << "直接调用:" << endl;
 
  p.area();
  r.area();
  c.area();
  cout << "通过calearea函数调用:" << endl;
  calcarea(&p);
  calcarea(&r);
  calcarea(&c);
 
  return 0;
}

虚函数定义的说明

1.派生类应该从它的基类公有派生

2.必须先在基类中定义虚函数(并不一定是最高层的基类)

3.在派生类中重写虚函数是virtual可以写也可以不写

4.一个虚函数无论被继承多少次,都保持其虚函数特性

5.虚函数必须在其类的成员函数中,不能是友元函数,不能是静态成员函数

6.内联函数不能是虚函数

7.构造函数不能是虚函数

8.析构函数可以是虚函数


相关文章
|
2月前
|
监控 Linux C++
4步实现C++插件化编程,轻松实现功能定制与扩展(2)
本文是《4步实现C++插件化编程》的延伸,重点介绍了新增的插件“热拔插”功能。通过`inotify`接口监控指定路径下的文件变动,结合`epoll`实现非阻塞监听,动态加载或卸载插件。核心设计包括`SprDirWatch`工具类封装`inotify`,以及`PluginManager`管理插件生命周期。验证部分展示了插件加载与卸载的日志及模块状态,确保功能稳定可靠。优化过程中解决了动态链接库句柄泄露问题,强调了采纳用户建议的重要性。
91 27
4步实现C++插件化编程,轻松实现功能定制与扩展(2)
|
7月前
|
存储 C++ UED
【实战指南】4步实现C++插件化编程,轻松实现功能定制与扩展
本文介绍了如何通过四步实现C++插件化编程,实现功能定制与扩展。主要内容包括引言、概述、需求分析、设计方案、详细设计、验证和总结。通过动态加载功能模块,实现软件的高度灵活性和可扩展性,支持快速定制和市场变化响应。具体步骤涉及配置文件构建、模块编译、动态库入口实现和主程序加载。验证部分展示了模块加载成功的日志和配置信息。总结中强调了插件化编程的优势及其在多个方面的应用。
887 88
|
4月前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
82 5
|
4月前
|
Serverless 编译器 C++
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
89 4
|
4月前
|
设计模式 IDE 编译器
【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】
本项目旨在通过C++编程实现一个教学游戏,帮助小朋友认识动物。程序设计了一个动物园场景,包含Dog、Bird和Frog三种动物。每个动物都有move和shout行为,用于展示其特征。游戏随机挑选10个动物,前5个供学习,后5个用于测试。使用虚函数和多态实现不同动物的行为,确保代码灵活扩展。此外,通过typeid获取对象类型,并利用strstr辅助判断类型。相关头文件如&lt;string&gt;、&lt;cstdlib&gt;等确保程序正常运行。最终,根据小朋友的回答计算得分,提供互动学习体验。 - **任务描述**:编写教学游戏,随机挑选10个动物进行展示与测试。 - **类设计**:基类
87 3
|
5月前
|
算法 网络协议 数据挖掘
C++是一种功能强大的编程语言,
C++是一种功能强大的编程语言,
109 14
|
7月前
|
存储 编译器 程序员
C++类型参数化
【10月更文挑战第1天】在 C++ 中,模板是实现类型参数化的主要工具,用于编写能处理多种数据类型的代码。模板分为函数模板和类模板。函数模板以 `template` 关键字定义,允许使用任意类型参数 `T`,并在调用时自动推导具体类型。类模板则定义泛型类,如动态数组,可在实例化时指定具体类型。模板还支持特化,为特定类型提供定制实现。模板在编译时实例化,需放置在头文件中以确保编译器可见。
89 11
|
7月前
|
C++
C++ 20新特性之结构化绑定
在C++ 20出现之前,当我们需要访问一个结构体或类的多个成员时,通常使用.或->操作符。对于复杂的数据结构,这种访问方式往往会显得冗长,也难以理解。C++ 20中引入的结构化绑定允许我们直接从一个聚合类型(比如:tuple、struct、class等)中提取出多个成员,并为它们分别命名。这一特性大大简化了对复杂数据结构的访问方式,使代码更加清晰、易读。
94 0
|
9月前
|
编译器 C++ 索引
C++虚拟成员-虚函数
C++虚拟成员-虚函数
|
10月前
|
编译器 C++ 运维
开发与运维函数问题之函数的返回类型如何解决
开发与运维函数问题之函数的返回类型如何解决
65 6

热门文章

最新文章