C++ 笔记10 | 多态(polymorphic)

2023-05-16

eg:实现图形库,用于显示各种图形
				 图形基类(位置、绘制)
				/					\
	矩形子类(宽和高、绘制)		圆形子类(半径、绘制)	

#include <iostream>
using namespace std;
class Shape{//图形基类
public:
    Shape(int x = 0,int y = 0):m_x(x),m_y(y){}
    virtual void draw(void){//虚函数
        cout << "绘制图形:" << m_x << "," << m_y << endl;
    }
protected:
    int m_x;//x坐标
    int m_y;//y坐标
};

class Rect:public Shape{//矩形子类
public:
    Rect(int x,int y,int w,int h):Shape(x,y),m_w(w),m_h(h){}
    void draw(void){//自动变成虚函数
        cout << "绘制矩形:" << m_x << "," << m_y << "," << m_w
            << "," << m_h << endl;
    }
private:
    int m_w;//宽度
    int m_h;//高度
};

class Circle:public Shape{//圆形子类
public:
    Circle(int x,int y,int r):Shape(x,y),m_r(r){}
    void draw(void){//自动变成虚函数
        cout << "绘制圆形:" << m_x << "," << m_y << "," << m_r
            << endl;
    }
private:
    int m_r;
};
void render(Shape* buf[]){
    //正常通过指针调用成员函数根据指针的类型去调用;但是如果调用的是
    //虚函数,不再根据指针本身类型而是根据实际指向的目标对象类型调用
    for(int i=0;buf[i]!=NULL;i++)
        buf[i]->draw();
}
int main(void){
    Shape* buf[1024] = {NULL};//用户缓冲区
    buf[0] = new Rect(1,2,3,4);//向上造型
    buf[1] = new Circle(5,6,7);
    buf[2] = new Rect(11,22,13,14);
    buf[3] = new Circle(15,16,71);
    buf[4] = new Rect(8,12,33,41);
    buf[5] = new Circle(18,66,9);
    render(buf);
    return 0;
}

二十二 多态(polymorphic)

1 虚函数覆盖(函数重写)、多态概念

1)如果基类中某个成员函数被声明为虚函数,那么子类中和该函数具有相同的成员函数就也是虚函数,并且对基类中版本形成覆盖,即函数重写。

2)满足虚函数覆盖关系后,这时通过指向子类对象的基类指针或者通过引用子类对象的基类引用,去调用虚函数,实际被执行的将是子类中的覆盖版本,而不是基类中的原始版本,这种语法现象被称为多态。

class Base{
	public:
		virtual void func(void){}//虚函数
	};
	class Derived:pubilc Base{
		void func(void){}//也是虚函数
	}	
	Derived d;
	Base* pb = &d;//pb指向子类对象的基类指针
	Base& rb = d;//rb引用子类对象的基类引用
	pb->func();//实际被执行的将是子类中的覆盖版本
	rb.func();//实际被执行的将是子类中的覆盖版本

2 虚函数覆盖(函数重载)条件

1)只有类中成员函数才能被声明为虚函数,而全局函数、静态成员函数、构造函数都不能声明为虚函数。
注:析构函数可以为虚函数(特殊,后面讲)

2)只有在基类中以virtual关键字修饰的成员函数才能作为虚函数被子类中版本覆盖,而与子类中虚函数是否加virtual关键字无关。

3)虚函数在基类中的原始版本和子类中的覆盖版本必须具有相同的函数签名,即函数名、参数表(参数类型和个数)和常属性一致。

#include <iostream>
using namespace std;
class A{};
class B:public A{};
class Base{
public:
    virtual void func(int i = 100)const{
        cout << "Base的func" << endl;
    }
    virtual /*A**/A& foo(void){//
        cout << "Base的foo" << endl;
    }
};

class Derived:public Base{
public:
    /*virtual*/ void func(int j = 200)const{//子类中加virtual或不加是没影响的
        cout << "Derived的func" << endl;
    }
    /*B**/B& foo(void){//
        cout << "Derived的foo" << endl;
    }
};
int main(void){
    Derived d;
    Base* pb = &d;//pb:指向子类对象的基类指针
    pb->func();
    pb->foo();
    return 0;
}

4)如果基类中的虚函数返回基本类型的数据,那么该函数在子类中覆盖版本必须返回相同类型的数据;而如果基类中的虚函数返回类类型的指针(A*)或引用(A&),那么允许子类中的覆盖版本返回其子类类型的指针(B*)或引用(B&)。
class A{};
class B:public A{};

3 多态的条件

1)多态的语法现象除了满足虚函数覆盖,还必须通过指针或引用调用虚函数才能表现出来。

#include <iostream>
using namespace std;
class Base{
public:
    virtual int cal(int x,int y){
        return x + y;
    }
    //void func(Base* this=&d)
    void func(void){
        //cout << this->cal(10,20) << endl;
        cout << cal(10,20) << endl;//200,有多态现象,通过影藏的this指针
    }
};
class Derived:public Base{
public:
    int cal(int x,int y){
        return x * y;
    }
};
int main(void){
    Derived d;
    //Base b = d;//必须是执政或引用
    //cout << b.cal(10,20) << endl;//30,没有多态现象
    d.func();//func(&d)//通过这样的方式也可以
    return 0;
}

2)调用虚函数的指针也可以是this指针,如果通过子类对象调用基类中的成员函数,在该成员函数中的this指针将是一个指向子类对象的基类指针,再使用this去调用虚函数,也可以表现多态的语法现象。//重点掌握

eg:Qt中的多线程
	class QThread{//线程类,官方写好的类
	public:
		void start(void){//开启线程
			this->run();
		}
	protected:
		virtual void run(void){//线程入口函数
		}
	};
	
	class MyThread:public QThread{
	protected:
		void run(void){//重写线程入口函数
			//需要放在线程执行的代码
		}
	};
	MyThread thread;
	thread.start();//开启子线程,重写的run函数将在子线程中被执行

4 纯虚函数、抽象类和纯抽象类

1)纯虚函数
virtual 返回类型 函数名(形参表)[const] = 0;
2)抽象类
如果类中包含了纯虚函数,那么该类就是抽象类。

  • 注:抽象类不能创建对象.

3)纯抽象类(接口、接口类)
如果类中所有的成员函数都是纯虚函数,那么该类就是纯抽象类。

#include <iostream>
using namespace std;
class Shape{//图形基类,抽象类,纯抽象类
public:
    Shape(int x = 0,int y = 0):m_x(x),m_y(y){}
    virtual void draw(void) = 0;//纯虚函数
protected:
    int m_x;//x坐标
    int m_y;//y坐标
};
class Rect:public Shape{//矩形子类
public:
    Rect(int x,int y,int w,int h):Shape(x,y),m_w(w),m_h(h){}
    void draw(void){//自动变成虚函数
        cout << "绘制矩形:" << m_x << "," << m_y << "," << m_w
            << "," << m_h << endl;
    }
private:
    int m_w;//宽度
    int m_h;//高度
};
class Circle:public Shape{//圆形子类
public:
    Circle(int x,int y,int r):Shape(x,y),m_r(r){}
    void draw(void){//自动变成虚函数
        cout << "绘制圆形:" << m_x << "," << m_y << "," << m_r
            << endl;
    }
private:
    int m_r;
};
void render(Shape* buf[]){
    //正常通过指针调用成员函数根据指针的类型去调用;但是如果调用的是
    //虚函数,不再根据指针本身类型而是根据实际指向的目标对象类型调用
    for(int i=0;buf[i]!=NULL;i++)
        buf[i]->draw();
}
int main(void){
    Shape* buf[1024] = {NULL};
    buf[0] = new Rect(1,2,3,4);
    buf[1] = new Circle(5,6,7);
    buf[2] = new Rect(11,22,13,14);
    buf[3] = new Circle(15,16,71);
    buf[4] = new Rect(8,12,33,41);
    buf[5] = new Circle(18,66,9);
    render(buf);

    //Shape s;//error//抽象类不能创建对象

    return 0;
}

代码设计原理:

#include <iostream>
using namespace std;
class PDFParser{
public:
    void parse(const char* pdffile){
        cout << "解析出一行文本" << endl;
        onText();
        cout << "解析出一幅图片" << endl;
        onImage();
        cout << "解析出一个矩形" << endl;
        onRect();
    }
private:
    virtual void onText(void) = 0;
    virtual void onImage(void) = 0;
    virtual void onRect(void) = 0;
};

class PDFRender:public PDFParser{
private:
    void onText(void) {
        cout << "显示一行文本" << endl;
    }
    void onImage(void) {
        cout << "绘制一幅图片" << endl;
    }
    void onRect(void) {
        cout << "绘制一个矩形" << endl;
    }
};
int main(void){
    PDFRender render;
    render.parse("xx.pdf");
    return 0;
}

5 多态原理//了解

通过虚函数表和动态绑定实现了多态的语法//参考下图
多态原理图

1)虚函数表会增加内存的开销(4字节指针)
2)动态绑定有时间的开销
3)虚函数不能被内联优化
总结:实际开发中如果没有多态的语法要求,最好不要使用虚函数。

6 虚析构函数

问题:基类析构函数不会调用子类的析构函数,如果对一个指向子类对象的基类指针使用delete运算,实际被执行的将是基类的析构函数,子类的析构函数不会被执行,有内存泄漏风险

解决:如果将基类中的析构函数声明为虚函数,那么子类中的析构函数也就是一个虚析构函数,并且可以对基类中版本形成覆盖,可以表现多态的语法;这时如果对一个指向子类对象的基类指针使用delete运算符,实际被执行的将是子类的析构函数,子类的析构函数在执行结束后又会自动调用基类的析构函数,从而避免内存泄漏。

#include <iostream>
using namespace std;
class Base{
public:
    Base(void){
        cout << "基类动态资源分配" << endl;
    }
    virtual ~Base(void){//虚析构函数
        cout << "基类动态资源释放" << endl;
    }
};
class Derived:public Base{
public:
    Derived(void){
        cout << "子类动态资源分配" << endl;
    }
    ~Derived(void){//虚析构函数
        cout << "子类动态资源释放" << endl;
    }
};
int main(void){
    Base* pb = new Derived;
    //...
    //1)pb->析构函数
    //2)释放内存
    delete pb;
    pb = NULL;
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 笔记10 | 多态(polymorphic) 的相关文章

  • 【SLAM】ORB-SLAM3解析——综述(1)

    之前学习VINS和LIO SAM的时候都是代码流 xff0c 不是很重视看论文 xff0c 现在有空学ORB SLAM3了 xff0c 这一次 xff0c 先看一下论文 考虑到边上班边学 xff0c 更新的会比较慢 看完论文之后 xff0c
  • 【SLAM】LVI-SAM解析——综述

    LVI SAM可以认为是LIO SAM和VINS MONO的合体 xff0c 在此基础上的修改不大 github xff1a https github com TixiaoShan LVI SAM paper LVI SAM Tightly
  • 【SLAM】DM-VIO(ros版)安装和论文解读

    1 dm vio的安装 进入官方链接 xff0c 基本上就是按照readme的操作来 xff1a 下载代码 xff1a git clone https github com lukasvst dm vio git 安装gtsam xff1a
  • 【SLAM】SVO2.0编译运行和论文代码解读

    SVO2是我遇到的最难编译的代码了 xff0c 毕竟公开版本怎么能放最好的代码呢 xff0c 搞了快一个星期 xff0c 终于跑起来了 github xff1a GitHub uzh rpg rpg svo pro open 论文 xff1
  • 【工具篇】postman的完全使用,全是干货

    目录 一 x1f347 GET请求 二 x1f348 POST请求 三 x1f349 PUT请求 四 x1f34a DELETE请求 五 x1f34b Headers 六 x1f34c 认证 xff08 1 xff09 基本认证 xff1a
  • GPS全球定位系统构成及原理

    GPS全球定位系统构成及原理 全球定位系统 GPS 是本世纪70年代由美国陆海空三军联合研制的新一代空间卫星导航定位系统 其主要目的是为陆 海 空三大领域提供实时 全天候和全球性的导航服务 xff0c 并用于情报收集 核爆监测和应急通讯等一
  • Visual C++设计UDP协议通讯示例

    下载本文源代码 一 绪言 UDP是一种面向非连接 xff0c 不可靠的通讯协议 xff0c 相对于 TCP来说 xff0c 虽然可靠性不及 xff0c 但传输效率较高 所以在网络上仍有很大的用途 这几日需要了解下udp通讯的过程 xff0c
  • 树莓派控制舵机云台

    文章目录 学习记录舵机代码分析 学习记录 手上有一套电赛时候买的舵机云台 扭矩15KG 型号为LD 1501MG 舵机 两个LD 1501MG舵机角度范围都是180度 控制的PWM波周期为20ms 角度与正脉冲宽度映射如下所示 0 5ms
  • jetson nano 编译pyrealsense2 运行t265

    在win10 的环境下 xff0c 安装pyrealsense2 xff0c 只需要简单的pip就可以了 xff0c 没想到在jetson nano下却花了那么多时间和精力 其实解决方案网上都有 xff0c 但是比较零碎 xff0c 只能遇
  • openmv探索_4_AprilTag标记追踪

    原理及代码 AprilTag标记追踪 空间坐标系的建立 以镜头中心为坐标系原点 xff0c 建立空间坐标系 图2 1 空间坐标系 旋转角度 xff08 参考系是上图中的坐标系 xff09 1 初始状态 图3 1 物体摆放的初始位置 上图的
  • Xsens MTi传感器 ROS下配置

    Xsens MTi传感器 ROS 1 概述2 MTI设置 MTmanager3 ROS下信息发布 MTSDK4 找不到设备 xff15 参考链接 xff16 延伸阅读传感器配置节点程序分析经典SLAM 1 概述 主要介绍Ubuntu下对Xs
  • 雷达点云 PointCloud2 格式转换

    雷达点云 sensor msgs PointCloud2 pcl PointCloud 数据格式转换参考代码 官方对点云格式的介绍 xff0c 主要有四种 xff0c sensor msgs PointCloud已经弃用 参考 sensor
  • Gmapping 之安装与配置

    Gmapping 之安装与配置 1 概述2 安装2 1 github仓库 3 运行3 1 运行截图3 2 重采样时的提示信息3 3 tf树 96 rosrun rqt tf tree rqt tf tree 96 3 4 所有的坐标变换信息
  • Gmapping Dropped 100.00% of messages so far 解决办法

    Dropped 100 00 of messages so far解决办法 概述显示tf树正常情况报错情况参考链接Gmapping 概述 运行Gmappping时出现错误提示 xff0c 其他参数 xff1a 话题等都设置正确了 xff0c
  • LOAM SLAM安装与配置

    LOAM SLAM安装与配置 1 概述2 安装3 运行4 结果5 其他6 参考链接 1 概述 简单介绍LOAM SLAM的安装与配置 xff0c 希望能帮助大家 xff0c 同时供自己以后参考 2 安装 运行LOAM需要PCL等库支持 xf
  • 什么是MSB、LSB,什么是大端、小端,区别是什么?

    MSB是Most Significant Bit的缩写 xff0c 最高有效位 在二进制数中 xff0c MSB是最高加权位 与十进制数字中最左边的一位类似 通常 xff0c MSB位于二进制数的最左侧 xff0c LSB位于二进制数的最右
  • ROS报错 cant locate node 解决办法

    ROS报错 can 39 t locate node 解决办法 1 概述2 原因3 解决办法4 其他 1 概述 ROS突然出现不能运行节点的问题 xff0c 这里简单介绍一下自己的解决办法 这个方法还是有缺陷 xff0c 更新方法在ROS编

随机推荐

  • ubuntu 开机后 按键 鼠标不能用

    ubuntu 开机后 按键 鼠标不能用 1 现象2 解决办法3 参考链接 1 现象 版本ubuntu 1804安装bumblebee后 xff0c 重启机器出现不能使用鼠标 键盘的现象 xff0c 具体点就是 xff0c 只有开机后的桌面显
  • 相机标定之使用ROS节点程序

    相机标定之使用ROS节点程序 1 概述2 准备3 步骤4 相关链接 1 概述 简单介绍如何使用ROS节点程序进行针孔模型相机标定的步骤 xff0c 供自己以后参考 xff0c 同时希望给大家带来帮助 2 准备 提前准备好棋盘格并打印出来 x
  • 相机标定之使用Matlab工具箱

    相机标定之使用Matlab工具箱 1 概述2 准备3 步骤4 相关链接 1 概述 简单介绍如何使用Matlab进行针孔模型相机标定的步骤 xff0c 供自己以后参考 xff0c 同时希望给大家带来帮助 2 准备 提前准备好棋盘格并打印出来
  • QT安装教程

    QT安装教程 1 概述2 下载安装2 1 下载2 2 安装2 3 安装配置其他环境2 4 安装成功2 5 问题及解决 3 参考链接 1 概述 简单介绍如何在Ubuntu下安装QT xff0c 希望能够帮助大家 xff0c 同时给自己一个参考
  • C++面向过程

    参考文档 xff1a C 43 43 教程 C 43 43 简介 概述 C 43 43 是一种静态类型的 编译式的 通用的 大小写敏感的 不规则的编程语言 xff0c 支持过程化编程 面向对象编程和泛型编程 C 43 43 是 C 的一个超
  • vector 指针 的指针

    vector 不能用指针 xff0c 用指针 xff0c push back会报错 xff0c size 也不对 vector本身用对象 xff0c 内容 简化为A 可以用指针 xff0c 当指针对象中还有指针B时 xff0c 就要用new
  • cmake find_package opencv 找不到

    目录 cmakelist设置方法ok 环境变量设置方法ok linux写法 cmakelists txt完整示例 find opencv lib find package OpenCV REQUIRED NO MODULE should b
  • liveplayer

    npm i 64 liveqing liveplayer S lt template gt lt div class 61 34 live player 34 gt lt div style 61 34 width 640px height
  • DLT algorithm needs at least 6 points for pose estimation from 3D-2D point correspondences. (expecte

    DLT algorithm needs at least 6 points for pose estimation from 3D 2D point correspondences expected 39 count gt 61 6 39
  • 关于OSD

    OSD的主要实现方法和类型 目前有两种主要的OSD实现方法 xff1a 外部OSD发生器与视频处理器间的叠加合成 xff1b 视频处理器内部 支持OSD xff0c 直接在视频缓存内部叠加OSD信息 外部OSD发生器与视频处理器间的叠加合成
  • RTK使用笔记-千寻CORS模式

    一 千寻CORS模式 与基站 43 接收机1对1相比 xff0c 优点为携带方便 xff0c 也不用考虑10公里移动基站问题 xff1b 缺点为第一千寻CORS模式有自己基站涵盖范围 xff0c 所以需要提前确定好范围 xff08 下文有介
  • c/c++语言实现登陆界面

    C C 43 43 语言实现登陆界面 整体功能介绍 实现一个登陆界面 1 输出一个登陆界面 2 用户名能够实现邮箱验证 xff0c regex库 xff0c 密码要不可见 3 进度条的模拟实现 4 音乐播放 分步实现 1 输出一个登陆界面
  • _getch()函数的介绍

    getch 函数的介绍及实例演示 我们一般所使用的 getchar 函数在读入一个字符时必须按一下 Enter 键 xff0c 该代码才会继续运行 但是 getch 函数读入一个字符时 不用 enter 代码会继续跑 xff1b 最简单使用
  • 嵌入式常见面试题

    嵌入式LINUX常见面试问题总结 声明 xff1a 本文是将常见的面试问题进行汇总 xff0c 但大多数问题也是开发中较为常见的技术盲区 xff01 在此进行了汇总 xff0c 以便后续进行参考 xff01 所有的答案部分是自己编写 xff
  • LINUX基础试题大全(1)

    说明 xff1a 此文章由于题数庞大 xff0c 为方便阅读本人将其分为四篇文章为大家分享 xff01 答案会今后不断进行更新 xff01 LINUX基础试题大全 xff08 1 xff09 填空题题 LINUX基础试题大全 xff08 2
  • C语言 | 深入学习数组

    说明 xff1a 本文主要讨论一维数组 xff0c 适宜程度 xff1a 对C语言初步认识及想深入学习者 1 从编译器角度理解数组 从编译器角度理解来讲 xff0c 数组也是一个变量 xff0c 和普通的变量没有本质的区别 变量的本质指的是
  • C语言 结构体 联合体 | 嵌套使用

    一 简单的实例分析 题目 xff1a 获取0x12345678各个字节 答案 xff1a span class token comment 方法一 span span class token macro property span clas
  • Linux 网络编程笔记3 | 内存 系统调用

    七 内存 1 虚拟内存 物理内存 半导体内存和换页文件 虚拟内存 xff1a 地址空间 xff0c 虚拟的存储区域 xff0c 应用程序所访问的都是虚拟内存 物理内存 xff1a 存储空间 xff0c 实际的存储区域 xff0c 只有系统内
  • 树莓派替代品

    51单片机 转载于 https www cnblogs com wanghuaqiang p 11481958 html
  • C++ 笔记10 | 多态(polymorphic)

    span class token variable eg 实现图形库 xff0c 用于显示各种图形 span span class token variable 图形基类 span span class token punctuation