首页 > 编程语言 >C++八股之函数重载与重写-静态多态与动态多态

C++八股之函数重载与重写-静态多态与动态多态

时间:2024-04-22 19:38:08浏览次数:23  
标签:函数 派生类 多态 C++ 纯虚 基类 重载 函数指针

重载:是指在同一作用域中允许存在多个同名函数,⽽这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。重载与类无关,重载实现编译时多态,属于静态绑定。
重写:指⼦类新定义⽗类的函数的做法。如果重写的函数在父类中是虚函数,那么能够实现动态多态。
如果在父类中没有将函数声明为虚函数,子类仍然可以重写(覆盖)父类的函数,但是在运行时将无法实现多态性,即无法通过父类指针或引用调用子类的函数。

什么叫运行时多态?父类指针或引用可以指向子类对象,并且如果父类中定义的虚函数被派生类重写,那么通过该指针或引用使用该函数时调用的是子类中重写后的函数。注意,必须通过虚函数才能实现动态多态。虚函数定义使用virtual关键字

点击查看代码
class Base {
public:
    virtual void print() {
        cout << "Base class" << endl;
    }
};

class Derived : public Base {
public:
    void print() override {
        cout << "Derived class" << endl;
    }
};

int main() {
    Base* basePtr = new Derived();
    basePtr->print();  // 输出 "Derived class"
    delete basePtr;
    return 0;
}

上述代码中使用virtual关键字是定义基类中的函数为虚函数,在子类中进行重写。重写虚函数时override关键字不是必须的,但是建议写上,这是一个良好的习惯。override关键字提供编译器级别的检查,若重载的不是虚函数同时又使用了override关键字,那么会抛出错误异常。

纯虚函数和虚函数的区别
纯虚函数(Pure Virtual Functions)是在基类中声明的没有实际实现的虚函数,它只是为了让派生类去实现。基类中包含纯虚函数的类被称为抽象类(Abstract Class),抽象类不能被实例化,只能作为接口使用。为了将函数声明为纯虚函数,需要在基类中使用关键字virtual,并将函数定义为纯虚函数,即在函数声明的末尾加上= 0。

点击查看代码
class Shape {
public:
    virtual void draw() = 0;  // 纯虚函数
};

class Circle : public Shape {
public:
    void draw() {
        cout << "Drawing a circle." << endl;
    }
};

int main() {
    // Shape shape;  // 错误,抽象类不能被实例化
    Shape* shapePtr = new Circle();
    shapePtr->draw();  // 输出 "Drawing a circle."
    delete shapePtr;
    return 0;
}

在一个类中可以同时定义纯虚函数和虚函数,被称为混合类。在包含纯虚函数的类中,该类仍然是一个抽象类,无法直接实例化。必须在派生类中对纯虚函数进行实现,才能实例化派生类对象。

点击查看代码
class Base {
public:
    virtual void virtualFunction() {
        cout << "Virtual function" << endl;
    }
    
    virtual void pureVirtualFunction() = 0;  // 纯虚函数
};

class Derived : public Base {
public:
    void virtualFunction() {
        cout << "Derived class's virtual function" << endl;
    }
    
    void pureVirtualFunction() {
        cout << "Derived class's implementation of pure virtual function" << endl;
    }
};

int main() {
    Base* basePtr = new Derived();
    basePtr->virtualFunction();       // 输出 "Derived class's virtual function"
    basePtr->pureVirtualFunction();   // 输出 "Derived class's implementation of pure virtual function"
    delete basePtr;
    return 0;
}

虚函数实现动态多态在编译器上如何实现
在编译器上,虚函数的实现主要涉及两个关键概念:虚函数表(vtable)和虚函数指针(vptr)。

虚函数表(vtable):

对于包含虚函数的类,在编译过程中,编译器会为该类生成一个虚函数表(vtable)。虚函数表是一个存储函数指针的数据结构,每个虚函数在表中都有一个对应的函数指针。虚函数表位于类的内存布局中,通常是以静态数据成员的形式存在于类的头部。虚函数表是针对类层次结构中的每个类生成的,每个类都有自己的虚函数表。

虚函数指针(vptr):

对于每个包含虚函数的类的对象,在内存中都会被分配一个额外的隐藏成员,称为虚函数指针(vptr)。如果子类继承的父类中包含虚函数,那么子类从逻辑上也包含虚函数,因此子类的对象也会有一个虚函数指针
虚函数指针是一个指向虚函数表的指针,它指向与对象所属的类对应的虚函数表。虚函数指针位于对象的内存布局中,通常是作为对象的第一个或前几个字节。通过虚函数指针,程序能够在运行时动态地确定调用哪个虚函数。

实现动态多态的过程如下:

  • 当使用基类指针或引用指向派生类对象时,编译器会根据指针或引用的静态类型(即基类类型)来访问虚函数。
  • 在运行时,通过虚函数指针(vptr)找到对象所属的虚函数表。
  • 通过虚函数表中的函数指针,确定要调用的虚函数的地址。
    最终,运行时会调用正确的派生类函数,实现多态性。

派生类如何继承和修改基类的虚函数表
派生类会继承并修改基类的虚函数表。
当一个派生类继承自一个基类时,它会继承基类的虚函数表。继承的过程中,派生类会保留基类的虚函数表,并在其后添加自己新增的虚函数的地址。如果派生类重写(覆盖)了基类的虚函数,它会将自己的虚函数的地址替换掉基类虚函数表中相应位置的地址。这样,通过派生类对象的虚函数指针,就可以调用派生类自己的虚函数。如果派生类新增了虚函数,它会将新增虚函数的地址添加到自己的虚函数表的末尾。这样,通过派生类对象的虚函数指针,就可以调用新增的虚函数。需要注意的是,派生类的虚函数表与基类的虚函数表是相互独立的,它们有不同的内存空间。派生类的虚函数表会继承基类的虚函数表,并在其基础上进行修改或扩展。

理解多态的关键在于virtual关键字,还是有很多深入讨论的内容,后续继续补充

标签:函数,派生类,多态,C++,纯虚,基类,重载,函数指针
From: https://www.cnblogs.com/starstxg/p/18151223

相关文章

  • UE4纯C++实现游戏中快捷栏之创建快捷栏UI
    作为一个在游戏界面中显示的快捷栏,我们需要在游戏运行时就显示出快捷栏UI,故我们创建两个Widget。1.SlAiGameHUDWidget:负责游戏中界面UI的整体显示2.SlAiShortcutWidget:负责快捷栏部件的显示与逻辑然后我们通过:1.将GameHUDWidget添加进视口:在GameHUD文件中添加Game......
  • 深度解读《深度探索C++对象模型》之数据成员的存取效率分析(三)
    接下来我将持续更新“深度解读《深度探索C++对象模型》”系列,敬请期待,欢迎关注!也可以关注公众号:iShare爱分享,自动获得推文和全部的文章列表。前面两篇请通过这里查看:深度解读《深度探索C++对象模型》之数据成员的存取效率分析(一)深度解读《深度探索C++对象模型》之数据成员的......
  • C++ 上位软件通过Snap7开源库访问西门子S7-1200/S7-1500数据块的方法
    前言本人一直从事C++上位软件开发工作较多,在之前的项目中通过C++访问西门子PLCS7-200/S7-1200/S7-1500并进行数据交互的应用中一直使用的是ModbusTCP/ModbusRTU协议进行。Modbus上位开源库采用的LibModbus。经过实际应用发现Modbus开源库单次发送和接受的数据不能超......
  • C++ 连接pg数据库
    环境:windows10vs2022引入pqxxs一些增删改查的示例代码#include"pqxx/pqxx"voidinsertPg(){try{//建立连接pqxx::connectionconn("dbname=postgresuser=postgrespassword=123hostaddr=127.0.0.1port=5432");//添加数据......
  • 关于c++输入输出缓冲区,和IO加速流的一些理解
    首先,让我们来介绍一下这个函数ios::sync_with_stdio();这个函数在缺省状态下默认为true,即开启,这个函数的作用是同步c和c++的缓冲区这个操作是c++为了兼容c而做出的保守决定,即将c和c++的缓冲区合并为一个,但是这样会带来性能上的开销为什么呢?因为这个兼容缓冲区先执行c的输入输......
  • C与C++的内存管理
    C中的malloc/relloc/calloc/free1.malloc与freemalloc函数用于分配指定大小的内存空间,并返回空间的首地址,若分配失败则返回NULL。free用来释放已分配的内存空间。intmain(){ int*ptr=(int*)malloc(sizeof(int)*10);//分配十个int型的空间 if(ptr==NULL){ pr......
  • C++U7-1-高精度加减
    学习目标    高精度加法        [高精度加法] #include<bits/stdc++.h>usingnamespacestd;intmain(){stringa;stringb;intc[10089]={0};intd[10089]={0};inte[10089]={0};cin>>a>>b;in......
  • 《Effective C++》读书笔记
    《EffectiveC++》读书笔记之前看过一遍,不过草草了事。近日看了《深度探索C++对象模型》,想起《EffectiveC++》中的内容已经有些忘记了,所以重新温习一下。这篇笔记只挑选书中的一些重要内容进行记录。条款07:为多态基类声明virtual析构函数这一个条款几乎是面试中的高频问题,只需......
  • 深度解读《深度探索C++对象模型》之数据成员的存取效率分析(二)
    接下来我将持续更新“深度解读《深度探索C++对象模型》”系列,敬请期待,欢迎关注!也可以关注公众号:iShare爱分享,自动获得推文和全部的文章列表。接下来的几篇将会讲解非静态数据成员的存取分析,讲解静态数据成员的情况请见上一篇:《深度解读《深度探索C++对象模型》之数据成员的存取......
  • C++ STL -- HashTable
    HashTable一般常用的unordered_set、unordered_map都是基于哈希表实现的,哈希表主要需要注意的是哈希冲突,迭代器等基础哈希映射使用哈希函数将键映射到索引的数据结构。即将原始数组索引通过哈希函数映射到一个哈希值上,从而将一个大范围索引,减小到一个小的固定范围哈希冲突......