当前位置:首页 C++ > 正文

C++中基类中使用虚函数与不使用的区别

作者:野牛程序员:2023-07-26 08:36:57 C++阅读 2629

在 C++ 中,基类(父类)中使用虚函数和不使用虚函数有很大的区别。虚函数是面向对象编程中一个重要的概念,它允许在派生类(子类)中覆盖基类中的函数,实现多态性(polymorphism)。以下是它们之间的主要区别:

  1. 虚函数允许运行时多态性:当在基类中声明一个虚函数,并在派生类中重写(覆盖)该函数时,程序在运行时会根据对象的实际类型来调用适当的函数版本。这意味着即使通过基类指针或引用调用虚函数,实际调用的是派生类中的函数。这种行为称为运行时多态性。

  2. 不使用虚函数的情况下,函数调用是静态绑定:如果在基类中声明一个非虚函数,并在派生类中重写该函数,通过基类指针或引用调用该函数时,会根据指针或引用的静态类型来确定调用哪个函数版本。这种行为称为静态绑定。

  3. 虚函数需要额外的存储和查找开销:虚函数表(vtable)是一种用于实现虚函数的机制,它使得运行时多态性成为可能。每个含有虚函数的类都会有一个虚函数表,其中存储着虚函数的地址。这样的额外开销在大型类层次结构中可能是显著的。

  4. 不使用虚函数可以避免虚函数表开销:如果你的类层次结构不需要运行时多态性,或者你关注性能并希望避免虚函数表的开销,可以选择不使用虚函数。

使用虚函数的典型应用场景是在基类中定义一个接口,然后在派生类中根据需要对该接口进行实现。这样的设计可以使得代码更加灵活和可扩展。

总之,虚函数的使用允许实现多态性和动态绑定,但会引入额外的开销。如果不需要多态性,或者对性能要求较高,可以选择不使用虚函数。


让我们通过一个简单的例子来说明使用和不使用虚函数的区别。

假设我们有一个基类 Shape 表示几何形状,它有一个计算面积的函数 CalculateArea()

  1. 使用虚函数的情况:

#include <iostream>

class Shape {
public:
    virtual double CalculateArea() const {
        return 0.0;
    }
};

class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}

    virtual double CalculateArea() const override {
        return 3.14159 * radius * radius;
    }
};

class Square : public Shape {
private:
    double side;

public:
    Square(double s) : side(s) {}

    virtual double CalculateArea() const override {
        return side * side;
    }
};

int main() {
    Circle circle(5.0);
    Square square(4.0);

    Shape* shape1 = &circle;
    Shape* shape2 = &square;

    std::cout << "Area of circle: " << shape1->CalculateArea() << std::endl;
    std::cout << "Area of square: " << shape2->CalculateArea() << std::endl;

    return 0;
}

输出:

Area of circle: 78.5398
Area of square: 16

在这个例子中,Shape 类中的 CalculateArea() 函数是虚函数。我们创建了 CircleSquare 两个派生类,并在每个派生类中重写了 CalculateArea() 函数。在 main() 函数中,我们通过基类指针指向不同的派生类对象,并调用 CalculateArea() 函数。由于虚函数的存在,程序在运行时会根据对象的实际类型调用适当的函数版本,从而正确计算出每个形状的面积。

  1. 不使用虚函数的情况:

#include <iostream>

class Shape {
public:
    double CalculateArea() const {
        return 0.0;
    }
};

class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}

    double CalculateArea() const {
        return 3.14159 * radius * radius;
    }
};

class Square : public Shape {
private:
    double side;

public:
    Square(double s) : side(s) {}

    double CalculateArea() const {
        return side * side;
    }
};

int main() {
    Circle circle(5.0);
    Square square(4.0);

    Shape* shape1 = &circle;
    Shape* shape2 = &square;

    std::cout << "Area of circle: " << shape1->CalculateArea() << std::endl;
    std::cout << "Area of square: " << shape2->CalculateArea() << std::endl;

    return 0;
}

输出:

Area of circle: 0
Area of square: 0

在这个例子中,Shape 类中的 CalculateArea() 函数没有使用 virtual 关键字,因此不是虚函数。当通过基类指针调用 CalculateArea() 函数时,由于静态绑定的特性,只会调用 Shape 类中的 CalculateArea(),而不是派生类中的重写版本。因此,输出结果都是 0.0。

这个例子展示了虚函数和非虚函数之间的区别。使用虚函数可以实现多态性,确保在运行时调用正确的函数版本,而不使用虚函数则会导致静态绑定,仅调用基类的函数。


野牛程序员教少儿编程与信息学奥赛-微信|电话:15892516892
野牛程序员教少儿编程与信息学竞赛-微信|电话:15892516892
相关推荐

最新推荐

热门点击