C/C++ - 类的多态机制

目录

多态概念

多态定义

多态的触发机制

虚函数

虚函数表

虚析构函

虚析构函数声明

虚析构函数的作用

纯虚函数

纯虚函数的声明

纯虚函数的作用

抽象类

多态原理

虚函数表 & 虚函数指针

继承机制下的虚函数表

动态绑定


  • 多态概念

    • 狗狗发出的声音为 -> 旺旺
    • 猫猫发出的声音为 -> 喵喵
    • 猫猫狗狗我们均可以理解为是动物类,但是其具体对象做同一功能有着不同的表现
  • 多态定义

    • 多态的触发机制

      • 在C++中,多态通过基类的指针或引用来触发。
      • 当通过基类指针或引用调用虚函数时,程序会在运行时确定实际对象的类型,并调用相应的函数。
      • 这种动态绑定的决策是通过虚函数表(vtable)来实现的。
    • 虚函数

      • 虚函数是在基类中声明的带有virtual​​​关键字的成员函数。
      • 虚函数通过动态绑定来实现多态,它可以在派生类中被重写。
      • 当通过基类的指针或引用调用虚函数时,实际调用的是指向或引用的对象的类型的版本。
    • 代码示例

      #include <iostream>
      
      class Animal {
      public:
          virtual void MakeSound() {
              std::cout << "Animal makes a sound." << std::endl;
          }
      };
      
      class Dog : public Animal {
      public:
          void MakeSound() override {
              std::cout << "Dog barks." << std::endl;
          }
      };
      
      class Cat : public Animal {
      public:
          void MakeSound() override {
              std::cout << "Cat meows." << std::endl;
          }
      };
      
      int main() {
          Animal* animalPtr;
          Dog dog;
          Cat cat;
      
          animalPtr = &dog;
          animalPtr->MakeSound();  // 输出: Dog barks.
      
          animalPtr = &cat;
          animalPtr->MakeSound();  // 输出: Cat meows.
      
          return 0;
      }
      
  • 虚函数表

    • 虚函数表是用于实现多态的关键机制之一。

    • 每个包含虚函数的类都有一个虚函数表,其中存储了虚函数的地址。

    • 每个对象都有一个指向其类的虚函数表的指针(通常称为虚函数指针或vptr)。

    • 当调用虚函数时,程序会根据对象的虚函数指针找到相应的虚函数表,并使用表中的地址调用正确的函数。

  • 虚析构函

    • 如果一个基类的析构函数是虚函数,那么派生类的析构函数也会自动成为虚函数。

    • 虚析构函数声明

      • 虚析构函数是在基类中声明的带有virtual​​​​​关键字的析构函数。
      • ​virtual ~ClassName();​​​​​
    • 虚析构函数的作用

      • 当通过基类指针删除派生类对象时,如果基类的析构函数是虚函数,则会根据实际对象的类型调用相应的析构函数。
      • 这样可以确保正确释放派生类对象所占用的资源,避免内存泄漏。
      • 虚析构函数只需要在基类中声明,派生类的析构函数会自动成为虚函数。
      • 虚析构函数应该是公有的(public),以便在派生类中可以正确访问和重写。
    • 代码示例

      • 假设我们有一个图形库,其中包含多个图形类,如矩形(Rectangle​​)、圆形(Circle​​)。我们希望能够存储这些图形对象,并能够对它们执行各种操作,比如计算总面积、打印每个图形的属性等。
      • #include <iostream>
        #include <vector>
        
        using namespace std;
        
        class Shape {
        public:
            virtual double getArea() const = 0;
            virtual void printInfo() const = 0;
            virtual ~Shape() {}
        };
        
        class Rectangle : public Shape {
        private:
            double length;
            double width;
        
        public:
            Rectangle(double length, double width) : length(length), width(width) {}
        
            double getArea() const override {
                return length * width;
            }
        
            void printInfo() const override {
                cout << "矩形,长:" << length << ",宽:" << width << endl;
            }
        
            ~Rectangle() {
                cout << "销毁矩形对象" << endl;
            }
        };
        
        class Circle : public Shape {
        private:
            double radius;
        
        public:
            Circle(double radius) : radius(radius) {}
        
            double getArea() const override {
                return 3.14159 * radius * radius;
            }
        
            void printInfo() const override {
                cout << "圆形,半径:" << radius << endl;
            }
        
            ~Circle() {
                cout << "销毁圆形对象" << endl;
            }
        };
        
        int main() {
            vector<Shape*> shapes;
            shapes.push_back(new Rectangle(5.0, 3.0));
            shapes.push_back(new Circle(4.0));
        
            double totalArea = 0.0;
            for (const auto& shape : shapes) {
                shape->printInfo();
                totalArea += shape->getArea();
            }
        
            cout << "总面积:" << totalArea << endl;
        
            for (const auto& shape : shapes) {
                delete shape;
            }
        
            return 0;
        }
        
  • 纯虚函数

    • 纯虚函数的声明

      • 纯虚函数是通过在基类中声明一个没有实际实现的虚函数来定义的。
      • ​virtual ReturnType functionName() = 0;​​
    • 纯虚函数的作用

      • 纯虚函数为基类提供接口,要求派生类实现该函数。
      • 派生类必须提供对纯虚函数的定义,以便成为具体类。
      • 纯虚函数使得基类成为抽象类,无法实例化对象。
    • 抽象类

      • 抽象类包含至少一个纯虚函数。
      • 抽象类无法实例化对象,只能用作基类。
      • 抽象类可以包含非纯虚函数,这些函数可以有实际的实现。
      • 抽象类定义了一组接口和基本行为,要求派生类实现纯虚函数。
    • 代码示例

      #include <iostream>
      
      class AbstractClass {
      public:
          virtual void PureVirtualFunction() = 0;  // 纯虚函数
      
          void NonPureVirtualFunction() {
              std::cout << "Non-pure virtual function" << std::endl;
          }
      };
      
      class ConcreteClass : public AbstractClass {
      public:
          void PureVirtualFunction() override {
              std::cout << "Pure virtual function implementation" << std::endl;
          }
      };
      
      int main() {
          // AbstractClass abstractObj;  // 错误,无法实例化抽象类对象
      
          ConcreteClass concreteObj;
          concreteObj.PureVirtualFunction();
          concreteObj.NonPureVirtualFunction();
      
          AbstractClass* abstractPtr;
          abstractPtr = &concreteObj;
          abstractPtr->PureVirtualFunction();
          abstractPtr->NonPureVirtualFunction();
      
          return 0;
      }
      
    • 抽象类不能实例化对象,只能用作基类。
    • 派生类必须实现纯虚函数才能成为具体类。
    • 如果派生类没有实现纯虚函数,它仍然被视为抽象类,无法实例化对象。
    • 抽象类可以包含非纯虚函数,但纯虚函数必须在派生类中实现。
    • 纯虚函数可以具有实现,但通常没有实际实现,只是提供一个接口。
  • 多态原理

    • 虚函数表 & 虚函数指针

      • #include <iostream>
        
        class Base
        {
        public:
        	int a = 1;
        
        	virtual void Fun1()
        	{
        
        	}
        };
        
        class Son : public Base
        {
        public:
        	int a = 1;
        
        	virtual void Fun1()
        	{
        
        	}
        };
        
        
        int main()
        {
        	Base b;
        	Son s;
        
        	return 0;
        }
        
      • 定义一个空的类,写一个虚函数,观察期内存大小?

        • 类中仅有一个虚函数时类的内存大小为4Byte

          • 虚函数指针 - virtual function ptr​​(类对象前4Byte)

            • ​​
          • ​virtual function ptr​​ - 虚函数表

            • ​​
          • FunAddr

            • ​​
          • 内存布局

            • ​​
    • 继承机制下的虚函数表

      • #include <iostream>
        #include <Windows.h>
        
        class Base1
        {
        public:
        	virtual void Fun1(){}
        };
        
        class Base2
        {
        public:
        	virtual void Fun2() {}
        };
        
        class Son : public Base1, public Base2
        {
        public:
        	virtual void Fun1() {}
        	virtual void Fun2() {}
        };
        
        int main()
        {
        	//虚函数表
        	Son obj;
        	std::cout << std::hex << *(PDWORD)((PCHAR)(&obj) + 0) << std::endl;
        	std::cout << std::hex << *(PDWORD)((PCHAR)(&obj) + 4) << std::endl;
        
        	return 0;
        }
        
      • ​​
      • ​​
      • ​​
      • ​​
      • ​​
      • ​​
      • ​​
    • 动态绑定

      • 示例1

        • 代码

          #include <iostream>
          
          class Animal {
          public:
              virtual void MakeSound() {
                  std::cout << "Animal makes a sound." << std::endl;
              }
          };
          
          class Dog : public Animal {
          public:
              void MakeSound() override {
                  std::cout << "Dog barks." << std::endl;
              }
          };
          
          class Cat : public Animal {
          public:
              void MakeSound() override {
                  std::cout << "Cat meows." << std::endl;
              }
          };
          
          int main() {
              Animal* animalPtr;
              Dog dog;
              Cat cat;
          
              animalPtr = &dog;
              animalPtr->MakeSound();  // 输出: Dog barks.
          
              animalPtr = &cat;
              animalPtr->MakeSound();  // 输出: Cat meows.
          
              return 0;
          }
          
          
        • 图解

          • 对象地址

          • 获取对象地址

          • 指向函数

      • 示例2

        • 代码

          #include <iostream>
          #include <Windows.h>
          
          class Base1
          {
          public:
          	virtual void Fun1() {}
          };
          
          class Base2
          {
          public:
          	virtual void Fun2() {}
          };
          
          class Son : public Base1, public Base2
          {
          public:
          	virtual void Fun1() {}
          	virtual void Fun2() {}
          };
          
          int main()
          {
          	Base1* pBase1;
          	Base2* pBase2;
          	Son obj;
          
          	pBase1 = &obj;
          	pBase2 = &obj;
          
          	pBase1->Fun1();
          	pBase2->Fun2();
          
          	return 0;
          }
          
        • 图解

          • Base1

          • Base2

          • pBase1 -> obj.Addr + 0 = vfptr(1)

            pBase2 -> obj.Addr + 4 = vfptr(2)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis -- String 字符串, 计数命令,字符串操作

"学如逆水行舟&#xff0c;不进则退。" 目录 Redis的String字符串 常见命令 set get mget mset setnx setex psetex 计数命令 incr incrby decr decrby incrbyfloat 字符串操作 append getrange setrange strlen 小结 string内部编码 Redis…

web应用课——(第四讲:中期项目——拳皇)

代码AC Git地址&#xff1a;拳皇——AC Git链接

迁移学习实现图片分类任务

导入工具包 import time import osimport numpy as np from tqdm import tqdmimport torch import torchvision import torch.nn as nn import torch.nn.functional as Fimport matplotlib.pyplot as plt %matplotlib inline# 忽略烦人的红色提示 import warnings warnings.fi…

11.Ubuntu

目录 1. 什么是Ubuntu 1.1. 概述 1.2. Ubuntu版本简介 1.2.1. 桌面版 1.2.2. 服务器版 2. 部署系统 2.1. 新建虚拟机 2.2. 安装系统 2.3. 部署后的设置 2.3.1. 设置root密码 2.3.2. 关闭防火墙 2.3.3. 启用允许root进行ssh 2.3.4. 安装所需软件 2.3.5. 制作快照 …

企业图纸数据防泄密方案(图纸防泄密的几个实用方法)

在当今的商业环境中&#xff0c;企业图纸数据的重要性日益凸显。无论是产品设计、研发、生产还是项目管理&#xff0c;图纸都承载着企业的核心知识产权和商业机密。 然而&#xff0c;随着信息技术的迅猛发展&#xff0c;企业图纸数据泄密事件屡屡发生&#xff0c;给企业带来了…

【20240131】USB相机(查看设备列表、打开设备)

USB相机采集 1、v4l2查看设备列表2、查看具体设备信息3、在桌面打开USB相机 1、v4l2查看设备列表 打开终端&#xff0c;输入&#xff1a;v4l2-ctl --list-devices usb设备在Webcam: Webcam栏&#xff0c;分别是video9和video10&#xff0c;下一步&#xff1a;确定哪一个是接入…

存内计算——发展史与近期成果

存内计算的概念早在上个世纪就已经被提出&#xff0c;但当时的人们寄希望于通过优化处理器设计以及工艺制程的升级&#xff0c;来获得性能和能效比的提升&#xff0c;存内计算的研究仅停留在理论阶段。随着大数据时代的到来&#xff0c;存内计算由于其结构特点以及摩尔定律的“…

机器学习 低代码 ML:PyCaret 的使用

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

大数加法-----蓝桥杯

题目描述 以字符串的形式读入两个数字&#xff0c;编写一个函数计算它们的和&#xff0c;以字符串形式返回。 数据范围: s.length,tlength < 100000&#xff0c;字符串仅由0~9构成要求: 时间复杂度 O(n)。 结构示例1&#xff1a; 结果示例2&#xff1a; 代码展示&#xff…

在中国,大多数做机器视觉工程师的人的出路是什么?

工程师一直号称是靠技术吃饭&#xff0c;越老越吃香。显然我们机器视觉工程师归属于工程师。 可是这种是一种低级趣味的思维力度。 试问现在程序员难道不是越老越吃香吗&#xff1f; 律师难道不是吗&#xff1f; 银行家难道不是吗&#xff1f; 现在整体经济下滑情况&#xff0…

C++ 菱形继承和虚拟菱形继承

菱形继承和虚拟菱形继承 菱形继承1. 概念2. 产生的问题 虚拟菱形继承1.1 使用1.2 原理 菱形继承 1. 概念 菱形继承是多继承的一个特殊情况&#xff0c;多继承是指一个子类类继承了两个或以上的直接父类&#xff0c;而菱形继承问题的产生是因为该子类的父类&#xff0c;继承了…

超好看的前端特效HTML特效、CSS特效、JS特效(第一期)

超好看的前端特效 1. 粒子组成文字动画特效 文件组成&#xff1a; base.css import url(https://fonts.googleapis.com/css?familyAbrilFatface|Raleway:300,400,900);.coidea-header {position: fixed;display: block;width: 96%;width: calc( 100% - 32px );height: 40px;m…

面试题:Feign第一次调用为什么会很慢?

文章目录 前言Ribbon是如何进行负载的RibbonClientConfigurationZoneAwareLoadBalancerRibbon负载均衡策略Ribbon-eager-load&#xff08;饥饿加载&#xff09;模式开启Ribbon饥饿加载总结 前言 首先要了解 Feign 是如何进行远程调用的&#xff0c;这里面包括&#xff0c;注册…

Fiddler修改https请求与响应 bug修复变灰了选不了等 Fiddle对夜神模拟器抓包设置

不要修改别人的东西&#xff0c;不要修改别人的东西&#xff0c;不要修改别人的东西 只用于自己的网站&#xff0c;自己安全调试。 fiddler修改https请求 1、打到要改的请求 2、替换请求内容 3、开启捕获。操作产生请求。 4、fiddler里查看请求或响应数据 &#xff0c;确认成…

ubuntu20配置mysql8

首先更新软件包索引运行 sudo apt update命令。然后运行 sudo apt install mysql-server安装MySQL服务器。 安装完成后&#xff0c;MySQL服务将作为systemd服务自动启动。你可以运行 sudo systemctl status mysql命令验证MySQL服务器是否正在运行。 连接MySQL 当MySQL安装…

操作系统基础:进程同步【下】

&#x1f308;个人主页&#xff1a;godspeed_lucip &#x1f525; 系列专栏&#xff1a;OS从基础到进阶 1 进程同步⛵1.1 吸烟者问题✈️1.1.1 问题描述✈️1.1.2 问题分析1.1.2.1 关系分析&#xff08;确定同步、互斥关系&#xff09;1.1.2.2 整理思路&#xff08;确定PV操作的…

Golang语言异常机制解析:错误策略与优雅处理

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站https://www.captainbed.cn/kitie。 前言 作为开发者来说&#xff0c;我们没办法保证程序在运行过程中永远不会出现异常&#xff0c;对于异常…

K8S网络

一、介绍 k8s不提供网络通信&#xff0c;提供了CNI接口(Container Network Interface&#xff0c;容器网络接口)&#xff0c;由CNI插件实现完成。 1.1 Pod通信 1.1.1 同一节点Pod通信 Pod通过虚拟Ethernet接口对&#xff08;Veth Pair&#xff09;与外部通信&#xff0c;Veth…

Unity_颜色空间GammaLinear

Unity_颜色空间Gamma&Linear Unity颜色空间的选择对于效果的影响具体有多大&#xff1f; 在ProjectSetting -> Player -> OtherSetting -> Rendering设置下的颜色空间选项卡选择颜色空间进行设置&#xff1a; 太深奥的解释一时半会看不懂&#xff0c;找见一个粗…

jsp 样衣申请与归还管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 样衣申请与归还管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境 为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为My…