当前位置: 首页 > article >正文

C++多态性

概念

C++中的多态性是面向对象编程的一个重要特征,它允许我们通过一个基类的指针或引用来操作不同派生类的对象。多态性增强了代码的灵活性和可扩展性。主要分为两种类型:编译时多态(静态多态)和运行时多态(动态多态)。

编译时多态(静态多态)

编译时多态通常通过函数重载和运算符重载实现。编译器在编译时决定调用哪个函数。

#include <iostream>  
using namespace std;  

class Print {  
public:  
    void display(int i) {  
        cout << "Displaying integer: " << i << endl;  
    }  
    void display(double d) {  
        cout << "Displaying double: " << d << endl;  
    }  
};  

int main() {  
    Print p;  
    p.display(5);     // 调用display(int)  
    p.display(5.5);   // 调用display(double)  
    return 0;  
}

运行时多态(动态多态)

运行时多态通过虚函数实现,允许在运行时根据对象的实际类型调用相应的函数。当我们定义一个虚函数并在子类中重写它时,基类的指针或引用可以指向子类的对象,并根据实际对象的类型调用相应的函数。

#include <iostream>  
using namespace std;  

class Base {  
public:  
    virtual void show() { // 虚函数  
        cout << "Base class show function called." << endl;  
    }  
    virtual ~Base() {} // 虚析构函数  
};  

class Derived : public Base {  
public:  
    void show() override { // 重写虚函数  
        cout << "Derived class show function called." << endl;  
    }  
};  

int main() {  
    Base* basePtr;          // 基类指针  
    Derived derivedObj;     // 派生类对象  
    basePtr = &derivedObj;  // 指向派生类对象  
    basePtr->show();        // 调用派生类的show()  
    
    return 0;  
}

原理

1.虚函数
虚函数是在基类中使用virtual关键字声明的函数,用于支持运行时多态。通过虚函数,可以在基类中定义接口,而在派生类中实现具体的逻辑。当使用基类指针或引用指向派生类对象并调用该虚函数时,C++会根据对象的真实类型来调用相应的函数实现。

2. 虚表(vtable)
每个包含虚函数的类在运行时会有一个虚表(vtable)。这个虚表是一个函数指针数组,存储了该类的所有虚函数的地址。当创建一个对象时,编译器会为该对象生成一个指向其类虚表的指针,称为虚指针(vptr)。

3. 运行时过程
运行时多态的过程大致如下:

  • 1.声明和定义:在基类中用virtual关键字声明虚函数,在派生类中重写该虚函数。

  • 2.对象创建:当基类指针或引用指向派生类对象时,派生类对象的虚指针会指向派生类的虚表。

3.函数调用:

当通过基类指针或引用调用虚函数时,程序会查找对象的虚表,找到对应虚函数的地址并执行。这是一个动态过程,因为决定调用哪个函数是在运行时而不是编译时。

#include <iostream>  
using namespace std;  

class Base {  
public:  
    virtual void show() {  // 虚函数  
        cout << "Base class show function called." << endl;  
    }  
    virtual ~Base() {}  // 虚析构函数  
};  

class Derived : public Base {  
public:  
    void show() override {  // 重写虚函数  
        cout << "Derived class show function called." << endl;  
    }  
};  

void demonstratePolymorphism(Base* b) {  
    b->show();  // 调用虚函数  
}  

int main() {  
    Base baseObj;        // 基类对象  
    Derived derivedObj;  // 派生类对象  

    demonstratePolymorphism(&baseObj);       // 调用基类的 show  
    demonstratePolymorphism(&derivedObj);    // 调用派生类的 show  
    
    return 0;  
}
  • 创建基类Base和派生类Derived,Derived重写了Base中的虚函数show。
  • 函数demonstratePolymorphism接受基类指针作为参数,调用show函数。实际上,将根据指针所指向的对象类型来调用相应的版本。
  • 当调用show时,即使传入的是基类指针,实际执行的是派生类的show。

重载,重写,隐藏

重载(Overloading)

重载是指在同一作用域内可以定义多个同名函数,只要它们的参数列表(参数的数量或类型)不同就可以。重载发生在同一个类中,也可以在继承关系中的不同类中的同名函数。

#include <iostream>  
using namespace std;  

class Math {  
public:  
    // 重载:不同参数数量和类型  
    int add(int a, int b) {  
        return a + b;  
    }  

    double add(double a, double b) {  
        return a + b;  
    }  
    
    int add(int a, int b, int c) {  
        return a + b + c;  
    }  
};  

int main() {  
    Math math;  
    cout << math.add(1, 2) << endl;        // 调用 add(int, int)  
    cout << math.add(1.5, 2.5) << endl;    // 调用 add(double, double)  
    cout << math.add(1, 2, 3) << endl;     // 调用 add(int, int, int)  
    
    return 0;  
}

在上面的例子中,add函数被重载,具有不同的参数类型和数量。

重写(Overriding)

重写是指在派生类中重新定义基类中已经声明的虚函数。重写允许派生类为基类的虚函数提供特定的实现。重写是在继承关系中发生的,它通常涉及到基类的虚函数和派生类的同名函数。

#include <iostream>  
using namespace std;  

class Base {  
public:  
    virtual void show() {  // 虚函数允许重写  
        cout << "Base class show function." << endl;  
    }  
};  

class Derived : public Base {  
public:  
    void show() override {  // 重写基类的虚函数  
        cout << "Derived class show function." << endl;  
    }  
};  

int main() {  
    Base* basePtr;  
    Derived derived;  
    basePtr = &derived;  
    
    basePtr->show();  // 调用 Derived::show()  
    
    return 0;  
}

在这段代码中,Derived类中的show函数重写了Base类中的虚函数show。通过基类指针调用时,会执行派生类的实现。

隐藏(Hiding)

隐藏是指在派生类中定义一个与基类中同名的成员(可以是函数或变量),这会遮蔽基类中的所有同名成员。隐藏不是重载,也不是重写,它发生在同一作用域内,并且会隐藏基类中的所有同名函数和变量,不论其参数和返回类型。

#include <iostream>  
using namespace std;  

class Base {  
public:  
    void func(int i) {  
        cout << "Base func with int: " << i << endl;  
    }  
    
    void func(double d) {  
        cout << "Base func with double: " << d << endl;  
    }  
};  

class Derived : public Base {  
public:  
    void func(int i) {  // 该函数隐藏了 Base 中的所有 named func  
        cout << "Derived func with int: " << i << endl;  
    }  
};  

int main() {  
    Derived d;  
    d.func(10);         // 调用 Derived::func(int)  
    // d.func(3.14);    // 错误:Base::func(double) 被隐藏,不能调用  
    
    return 0;  
}

在这个例子中,Derived类中的func(int)函数隐藏了Base类中的所有同名成员函数,尽管基类中还有一个func(double)。因此,尝试调用d.func(3.14)将导致编译错误。

总结

  • 重载(Overloading):同一个作用域中,可以有多个同名函数,参数列表不同。
  • 重写(Overriding):在派生类中重新定义基类中的虚函数,允许多态。
  • 隐藏(Hiding):在派生类中定义与基类中同名的成员,遮蔽基类中的所有同名成员。

http://www.kler.cn/a/428086.html

相关文章:

  • 【仓颉】仓颉编程语言Windows安装指南 配置环境变量 最简单解决中文乱码问题和其他解决方案大全
  • vscode+WSL2(ubuntu22.04)+pytorch+conda+cuda+cudnn安装系列
  • FPGA 使用 CLOCK_LOW_FANOUT 约束
  • 爬虫基础(一)HTTP协议 :请求与响应
  • 【新春特辑】2025年春节技术展望:蛇年里的科技创新与趋势预测
  • 机器人抓取与操作概述(深蓝)——1
  • 项目页面渲染学习总结
  • 【JavaWeb后端学习笔记】Spring全局异常处理器
  • 【论文笔记】Compact Language Models via Pruning and Knowledge Distillation
  • R155 VTA 认证对汽车入侵检测系统(IDS)合规要求
  • World of Warcraft (version update)
  • 蓝牙键鼠无法被电脑识别
  • 雨晨 2610(2)0.2510 Windows 11 24H2 Iot 企业版 LTSC 2024 极简 2in1
  • NOIP2011 普及组【瑞士轮】题解(AC)
  • vue2+html2canvas+js PDF实现试卷导出和打印功能
  • 解决Ubuntu在VMware关机时,老是一个光标在那里闪动几分钟,才能关机的问题
  • Docker 学习总结(84)—— Docker 常用运维命令
  • axios的get和post请求,关于携带参数相关的讲解一下
  • [0629].第29节:配置中心业务规则与动态刷新
  • 运维角度定位JAVA微服务线上CPU飙升问题
  • iOS如何操作更新推送证书
  • 基于单片机的智能客车超载监测综合性实验
  • Ubuntu 环境美化
  • 人机之间的交互存在不少逻辑性的跳跃
  • Hadoop3集群实战:从零开始的搭建之旅
  • 常用的8款电脑加密软件分享|2025电脑办公文件怎么加密?