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

文章介绍了C++中虚函数的概念及其在实现多态性、动态绑定、接口定义和析构函数中的应用。通过示例代码展示了如何利用虚函数实现基类指针调用派生类成员函数,以及在抽象基类中定义纯虚函数来定义接口规范。同时,提到了虚函数对内存和性能的影响。
摘要由CSDN通过智能技术生成

在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),这些数据结构占用了额外的内存空间。如果在程序中定义过多的虚函数或虚函数的层级比较深,将可能导致性能下降。因此,在使用虚函数时也要注意其对程序性能的影响。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

时雨h

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

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

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

打赏作者

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

抵扣说明:

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

余额充值