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;
}

在这个示例中,基类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;
}

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

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

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;
}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天选码农搬砖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值