C++中的override关键字

发布时间:2023-05-20

在C++11标准中,override是一个非常重要的关键字,可以让我们在代码编写中更加高效、简洁和安全。下面我们将从多个方面对override关键字进行详细的阐述。

一、override关键字的用途

override关键字的用途是告诉编译器,当前函数是对基类中的虚函数进行覆盖。在C++中,覆盖虚函数是一个非常常见的需求,它可以允许子类重新定义基类中的虚函数,从而实现多态性的目的。 例如:

class Base {
public:
    virtual void foo() {
        std::cout << "Base::foo()" << std::endl;
    }
};
class Derived : public Base {
public:
    void foo() override {   //使用override关键字,明确指出重新定义了基类中的虚函数
        std::cout << "Derived::foo()" << std::endl;
    }
};

在上述代码中,override关键字明确指明了Derived类中的foo()函数是对Base类中的虚函数foo()进行覆盖。这样,当我们对Derived类对象调用foo()函数时,会调用Derived类中的foo()函数而不是Base类中的foo()函数。

二、override关键字的语法要求

当使用override关键字定义覆盖的虚函数时,需要满足以下三个语法要求:

  1. 函数命名必须与基类中的虚函数命名相同。
  2. 函数参数类型、数量必须与基类中的虚函数完全一致。
  3. 函数返回类型必须与基类中的虚函数完全匹配,或者是其派生类。 例如:
class Base {
public:
    virtual int calculate(int a, int b) {
        return a + b;
    }
};
class Derived : public Base {
public:
    int calculate(int a, double b) override {   //参数类型、数量不匹配,编译错误
        return a * b;
    }
    double calculate(int a, int b) override {   //返回类型不匹配,编译错误
        return a + b;
    }
};

上述代码中,我们在Derived类中尝试覆盖Base类中的calculate()虚函数,但函数的参数类型、数量与基类中的calculate()虚函数不匹配,这会导致编译错误。

三、为什么要使用override关键字

使用override关键字的好处在于它可以避免在虚函数的覆盖中出现错误。如果没有override关键字,我们可能会无意中重新定义了一个新的函数而不是覆盖基类中的虚函数,这样会导致编译器无法检测到重载错误。 例如:

class Base {
public:
    virtual void foo(int n) {
        std::cout << "Base::foo(int)" << std::endl;
    }
};
class Derived : public Base {
public:
    void foo(double d) {   //无意中定义了一个新的函数,而不是覆盖基类中的虚函数
        std::cout << "Derived::foo(double)" << std::endl;
    }
};
int main() {
    Derived d;
    d.foo(1);    //调用了Derived类中的foo(double)函数,而不是覆盖了Base类中的foo(int)函数
}

上述代码中,我们在Derived类中定义了一个新的foo(double)函数,而不是覆盖Base类中的foo(int)函数。这样,在调用Derived类对象的foo()函数时,会调用Derived类中的foo(double)函数而不是覆盖了Base类中的foo(int)函数。这种错误很难检查和定位,使用override关键字可以很好地避免这种问题。

四、override关键字的实现细节

C++编译器实现override关键字的方式是,编译器会在派生类中寻找与基类中虚函数名称、参数类型、数量和返回类型完全匹配的函数,如果找到了,则将其视为覆盖了基类中的虚函数。 例如,在上述代码中,如果我们将Derived类中的foo(double)函数的参数类型修改为int,则编译器会将其视为覆盖了基类中的虚函数foo()。 另外,如果某一个函数被声明为override但是没有覆盖任何基类的虚函数,编译器会直接报错。 例如:

class Base {
public:
    virtual void foo() {
        std::cout << "Base::foo()" << std::endl;
    }
};
class Derived : public Base {
public:
    void bar() override {   //错误,没有覆盖任何基类的虚函数
        std::cout << "Derived::bar()" << std::endl;
    }
};

上述代码中,Derived类中的bar()函数声明为override,但是它并没有覆盖任何基类的虚函数,这会导致编译错误。

五、override关键字的应用实例

在实际的开发中,使用override关键字可以大大提高程序的效率、可读性和安全性。下面是一个使用override关键字的实例:

class Shape {
public:
    virtual void draw() const = 0;
};
class Circle : public Shape {
public:
    void draw() const override {   //覆盖了基类中的虚函数
        std::cout << "Circle::draw()" << std::endl;
    }
};
class Square : public Shape {
public:
    void draw() const override {   //覆盖了基类中的虚函数
        std::cout << "Square::draw()" << std::endl;
    }
};
int main() {
    std::vector<std::unique_ptr<Shape>> shapes;
    shapes.push_back(std::make_unique<Circle>());
    shapes.push_back(std::make_unique<Square>());
    for (auto& shape : shapes) {
        shape->draw();    //多态性的体现,调用不同派生类中的重载函数
    }
}

在上述代码中,我们定义了一个Shape类作为基类,并且声明了一个纯虚函数draw(),它表示不同的图形可以有不同的绘制方法。我们定义了两个派生类Circle和Square来覆盖基类中的虚函数draw(),它们分别代表圆形和正方形的绘制方法。然后我们在主函数中创建一个vector连接Shape类的智能指针,将Circle和Square对象加入其中,最后循环遍历该vector并调用draw()方法,这里会体现出多态性的特性,每次调用draw()方法都会根据对象的实际类型调用不同派生类中的draw()函数。

六、总结

在本文中,我们对C++中的override关键字做了详细的阐述,它可以让我们在代码编写中更加高效、简洁和安全。我们可以使用override关键字来覆盖基类中的虚函数,并且需要满足一定的语法要求,这样可以避免在虚函数的覆盖中出现错误。我们还介绍了override关键字的实现细节和应用实例,在实际的开发中,它可以大大提高程序的效率、可读性和安全性。