对C++中的虚函数的说明

news/2024/11/17 23:48:53/

在C++中,虚函数是对多态性(Polymorphism)的一种支持。当基类指针或引用被用来调用派生类对象的成员函数时,可以通过虚函数来实现动态绑定,即根据运行时类型确定要调用的函数。

下面是使用虚函数的两个典型场景:

  1. 实现多态

如果你需要在不同的子类上调用相同名字的函数,可以将该函数定义为虚函数。这样就可以使得程序在运行时按照其具体类型来选择调用哪个版本的函数,并实现多态性。

考虑如下代码:

class Animal {
public:virtual void speak() {cout << "This animal makes a sound." << endl;}
};class Dog : public Animal {
public:void speak() override {cout << "The dog barks." << endl;}
};class Cat : public Animal {
public:void speak() override {cout << "The cat meows." << endl;}
};int main() {Animal* animal1 = new Dog();Animal* animal2 = new Cat();animal1->speak(); // Output: The dog barks.animal2->speak(); // Output: The cat meows.delete animal1;delete animal2;return 0;
}

上述代码中,Animal类是一个基类,其中speak()函数被声明为虚函数。DogCat类都从Animal类派生而来,重新实现了speak()函数。在主函数中,我们分别用类型为Animal*的指针指向不同的子类对象,并调用了虚函数speak()。因为speak()是虚函数,所以调用的实际函数版本在运行时被动态地确定,从而实现了多态性。

  1. 实现动态绑定

当被重载的函数在程序运行时才能确定需要调用哪个版本时,使用虚函数非常有用。这种情况下,通过基类指针或引用调用该函数,编译器会根据其动态类型来选择调用正确的版本,也就是实现动态绑定的功能。

考虑如下代码:

class Car {
public:virtual void run() = 0;
};class GasCar : public Car {
public:void run() override {cout << "Gas car runs by burning gasoline." << endl;}
};class ElectricCar : public Car {
public:void run() override {cout << "Electric car runs by using battery power." << endl;}
};int main() {GasCar myGasCar;ElectricCar myElectricCar;Car* p1 = &myGasCar;Car& r1 = myElectricCar;p1->run(); // Output: Gas car runs by burning gasoline.r1.run(); // Output: Electric car runs by using battery power.return 0;
}

上述代码中,Car类是一个抽象基类,其中run()函数被声明为纯虚函数。GasCarElectricCar类都从Car类派生而来,重新实现了run()函数。在主函数中,我们分别创建了两个不同的子类对象,并将它们的地址存储到类型为基类Car*Car&的指针和引用中。通过这些指针和引用我们调用了虚函数run()。因为run()是纯虚函数,所以编译器会根据其动态类型来选择调用正确版本的函数。

除了上述场景,还有一些其他的情况下适合使用虚函数:

  1. 实现接口

在C++中,可以通过纯虚函数来定义接口规范,然后让子类来实现这些函数。使用纯虚函数定义接口可以使得代码更加清晰和易于维护。

考虑如下代码:

class Shape {
public:virtual double getArea() const = 0;
};class Rectangle : public Shape {
private:double width_;double height_;
public:Rectangle(double width, double height) :width_(width), height_(height) {}double getArea() const override {return width_ * height_;}
};class Circle : public Shape {
private:double radius_;
public:Circle(double radius) : radius_(radius) {}double getArea() const override {return 3.14159 * radius_ * radius_;}
};int main() {Shape* shapes[2] = { new Rectangle(4, 3), new Circle(2) };cout << "The areas of the shapes are:" << endl;for (int i = 0; i < 2; ++i) {cout << "Shape " << i + 1 << ": " << shapes[i]->getArea() << endl;delete shapes[i];}return 0;
}

上述代码中,Shape类是一个抽象基类,其中getArea()函数被声明为纯虚函数。RectangleCircle类都从Shape类派生而来,并重新实现了该纯虚函数。在主函数中,我们创建了一个包含两个不同形状对象的数组,并调用它们的虚函数getArea(),计算它们的面积。

  1. 实现析构函数

当类中涉及到继承时,子类中可能会定义一些新的资源。为了确保在释放基类时也能够正确地释放这些资源,可以将基类的析构函数定义为虚函数。这样,在删除通过指向基类指针或引用指向其派生类的对象时,会首先调用其派生类的析构函数,然后再调用基类的析构函数。

考虑如下代码:

class Base {
public:virtual ~Base() { cout << "Base destructor." << endl; }
};class Derived : public Base{
private:int* ptr_;
public:Derived(int value) : ptr_(new int {value}) {}~Derived() override {delete ptr_;cout << "Derived destructor." << endl;}
};int main() {Base* base = new Derived(42);delete base;return 0;
}

上述代码中,Base类是一个简单的基类,其中的析构函数被声明为虚函数。Derived类从Base类派生而来,重载了该析构函数。在主函数中,我们使用类型为指向基类Base*的指针指向类型为派生类Derived的对象,并对其进行删除操作。由于Base的析构函数被声明为虚函数,因此在删除派生类对象时会先调用其析构函数,然后再调用Base类的析构函数。

需要注意的是,虚函数存在虚表(Virtual Table)和虚指针(Virtual Pointer),这些数据结构占用了额外的内存空间。如果在程序中定义过多的虚函数或虚函数的层级比较深,将可能导致性能下降。因此,在使用虚函数时也要注意其对程序性能的影响。


http://www.ppmy.cn/news/471258.html

相关文章

装ubuntu18.04 未发现wifi适配器解决方法

问题 系统安装完之后&#xff0c;在设置>>Wi-Fi中显示&#xff1a;未发现WiFi适配器 替换网卡驱动 打开 软件和更新 >> 附加驱动&#xff0c;看看Broadcom栏里面有没有类似下面图里第二项这样的&#xff1a; 有的话就不用进行下一步了&#xff0c;直接勾选一个…

安装Ubuntu18.04后没有WIFI参数设置图标,无法连接wifi

问题描述&#xff1a; 我的电脑是联想拯救者Y9000P&#xff0c;23年的新机子&#xff0c;Win11系统Wifi使用正常&#xff0c;Ubuntu20.04的Wifi也能正常使用&#xff0c;唯独Ubuntu18.04安装后桌面右上角下拉菜单里没有Wifi设置参数选项&#xff0c;设置里Wi-Fi模块提示找不到…

win10蓝牙已配对安卓手机但连接不上 完美解决方法

win10蓝牙连接手机蓝牙&#xff0c;显示已配对但未连接 完美解决办法。 电脑&#xff1a;惠普暗影精灵台式机win10&#xff0c;版本号21H2 手机&#xff1a;安卓手机 1. win10蓝牙连接不是手机蓝牙基本上是驱动太老了&#xff0c;自带Windows没有更新&#xff0c;下载驱动更新…

惠普z6计算机进不去桌面,神舟Z6再降300大洋,气哭惠普暗影精灵

原标题&#xff1a;神舟Z6再降300大洋&#xff0c;气哭惠普暗影精灵 为了加大在暑促期间的竞争力&#xff0c;各大游戏本厂商纷纷推出自己的良心之作。其中&#xff0c;神舟在自家卖得最好的Z系列上&#xff0c;再添一款猛将。战神Z6-SL57D1被各大玩家称为配置最为科学的游戏本…

【MindSpore易点通】一站式指南

作者&#xff1a;王磊 更多精彩分享&#xff0c;欢迎访问和关注&#xff1a;https://www.zhihu.com/people/wldandan 目录 MindSpore易点通专栏是MindSpore易用性SIG打造的技术交流专区&#xff0c;用于收录MindSpore开发者的各种学习和实践经验总结。在此&#xff0c;我们整…

Unity3D新手入门初中高级教程

Unity Shader中级&#xff08;Unity2019 unity教程初级中级高级 扫码时备注或说明中留下邮箱 付款后如未回复请加微信 630105904 联系本博主 行业应用 编辑 播报 ATM领域应用&#xff08;汽车、运输、制造&#xff09; ATM领域应用(2张) 工业VR/AR的应用场景就是构建在数字…

解决Ubuntu更新nVidia显卡驱动后黑屏问题

跟大数据没关系&#xff0c;自己使用的一个记录。 现在已经完全抛弃windows了&#xff0c;完全用ubuntu来干活了。不是windows不好&#xff0c;是中国的windows生态环境太差了&#xff0c;随便装个国产小软件会附带给用户装一堆垃圾的东西&#xff0c;什么各种毒霸&#xff0c;…

[转载] Windows使用WakeOnLan配置【较详细】

背景&#xff1a; 个人神州笔记本 做wol 操作&#xff1a; 进入主机bios打开wakeonlan功能 网卡写静态IP地址并记录下mac地址 开始-控制面板-网络和共享中心(小图标)-更改适配器设置-本地连接-"双击" 设置网卡为支持魔术包唤醒 开始-控制面板-网络和共享中心(小…