• c++ virtual关键字


    1. 概述

    在C++中,virtual 是一个关键字,用于实现多态性(polymorphism)和动态绑定(dynamic binding)。它通常与类的成员函数一起使用,以允许在派生类中重写(override)基类的函数,并在运行时根据对象的实际类型调用正确的函数版本。

    2. 虚函数

    class Base {
    public:
        virtual void foo() {
            // 基类的虚函数实现
        }
    };
    
    class Derived : public Base {
    public:
        void foo() override {
            // 派生类的虚函数实现,重写了基类的虚函数
        }
    };
    
    int main() {
        Base* ptr = new Derived(); // 通过基类指针指向派生类对象
        ptr->foo(); // 动态绑定到派生类的虚函数
        delete ptr;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这个示例中,基类Base有一个虚函数foo(),派生类Derived重写了这个虚函数。通过使用基类指针指向派生类对象,可以在运行时调用派生类的虚函数,实现多态性和动态绑定。这使得程序在运行时能够根据对象的实际类型来调用适当的函数版本。

    3. 虚析构函数

    基类的虚构函数(也称为析构函数)之所以通常被定义为虚函数,是为了确保在多态性的情况下正确地销毁派生类对象。这涉及到C++中的对象生命周期和内存管理的重要问题。

    下面是为什么基类的析构函数应该定义为虚函数的原因:

    • 多态性和对象销毁:当你使用基类指针或引用来指向派生类对象时,C++具有动态绑定(dynamic binding)的特性,这意味着在运行时调用派生类的成员函数。这也包括销毁对象,因为销毁对象是通过调用析构函数来完成的。
    • 正确的对象销毁:如果基类的析构函数不是虚函数,那么在销毁派生类对象时,可能只会调用基类的析构函数而不会调用派生类的析构函数。这可能会导致资源泄漏或不正确的清理,因为派生类可能有自己特殊的资源需要释放。
    • 析构函数的链式调用:如果基类的析构函数是虚函数,那么在销毁派生类对象时,会首先调用派生类的析构函数,然后自动调用基类的析构函数。这确保了每个类的析构函数都会被适当地调用,以进行资源释放和清理工作。

    3.1 sample

    class Base {
    public:
        Base() {
            std::cout << "Base constructor" << std::endl;
        }
    
        virtual ~Base() { // 虚析构函数
            std::cout << "Base destructor" << std::endl;
        }
    };
    
    class Derived : public Base {
    public:
        Derived() {
            std::cout << "Derived constructor" << std::endl;
        }
    
        ~Derived() { // 没有虚析构函数
            std::cout << "Derived destructor" << std::endl;
        }
    };
    
    int main() {
        Base* ptr = new Derived();
        delete ptr; // 使用基类指针销毁派生类对象
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这个示例中,如果基类的析构函数不是虚函数,那么在使用基类指针销毁派生类对象时,只会调用基类的析构函数,而不会调用派生类的析构函数,这可能导致资源泄漏。但如果基类的析构函数是虚函数,那么在销毁对象时会正确地调用派生类的析构函数,从而释放派生类特定的资源。

    因此,为了确保在继承体系中正确地销毁对象并执行适当的清理操作,通常将基类的析构函数声明为虚函数是一个良好的实践。这样可以确保多态性在对象销毁时能够正常工作。

    4. 纯虚函数

    纯虚函数(Pure Virtual Function)是C++中的一种特殊类型的虚函数,它在基类中声明但没有提供实际的函数定义。纯虚函数的主要作用是定义一个接口,要求派生类必须提供实际的实现,这鼓励了多态性和代码规范化。

    4.1 纯虚函数的作用

    • 定义抽象基类(Abstract Base Class):包含至少一个纯虚函数的类被称为抽象基类。抽象基类的主要目的是定义一组接口规范,而不是提供具体的实现。它通常用于表示通用概念,而具体的实现则由派生类提供。
    • 强制派生类实现接口:通过将函数声明为纯虚函数,基类强制要求任何派生类都必须提供其自己的实现。这确保了所有派生类都能提供特定功能,从而保持了代码规范化和一致性。
    • 支持多态性:纯虚函数为多态性提供了基础。派生类可以根据自己的需求实现纯虚函数,使得基类指针或引用可以在运行时调用适当的派生类版本,实现多态性和动态绑定。
    • 提供接口文档:通过在基类中声明纯虚函数,你为派生类提供了一份接口文档,明确了它们应该提供的功能。这有助于其他程序员理解如何使用派生类,因为接口规范已经定义清楚。

    4.2 sample

    class Shape {
    public:
        virtual double area() const = 0; // 纯虚函数,没有实际实现
        virtual double perimeter() const = 0; // 纯虚函数,没有实际实现
    };
    
    class Circle : public Shape {
    private:
        double radius;
    
    public:
        Circle(double r) : radius(r) {}
    
        double area() const override {
            return 3.14159265359 * radius * radius;
        }
    
        double perimeter() const override {
            return 2 * 3.14159265359 * radius;
        }
    };
    
    int main() {
        Shape* shape = new Circle(5.0);
        double shapeArea = shape->area(); // 动态绑定到派生类的实现
        delete shape;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这个示例中,Shape 类是一个抽象基类,它定义了两个纯虚函数 area() 和 perimeter()。Circle 类是一个派生类,提供了这两个函数的具体实现。通过基类指针,我们可以在运行时调用 Circle 类的实现,实现多态性和动态绑定

  • 相关阅读:
    numpy==1.20.1安装不上
    jmeter集群搭建
    PyG学习 - Dataset, DataLoader, Tranforms
    零基础在家就可以做的副业,七个兼职项目推荐
    rollup打包工具快速入门
    【项目总结】C++ 云盘
    JavaScript流程控制分支
    约束布局ConstraintLayout基础使用
    微服务之网关路由
    C++——类和对象3|日期类型|Cout运算符重载|Cin运算符重载|const成员|
  • 原文地址:https://blog.csdn.net/weixin_47139576/article/details/133723532