override
是 C++11 引入的一个关键字,override
的作用是在派生类中显式地声明某个函数是用于重写基类的虚函数。它不仅仅是一个语法标记,更重要的是提供了编译时的错误检查功能,确保程序员确实按照预期在派生类中重写了基类的函数。如果没有正确地重写函数(如参数类型不匹配),编译器会抛出错误。
使用场景
当你在派生类中希望重写基类的虚函数时,通常在函数声明后加上 override
关键字。这可以确保:
- 基类中的函数是虚函数。
- 派生类中的函数正确地重写了基类中的函数(函数签名要完全一致)。
用法示例
假设有一个基类 Base
,其中定义了一个虚函数 virtual void func()
。
class Base {
public:
virtual void func() {
std::cout << "Base function" << std::endl;
}
};
如果要在派生类 Derived
中重写该虚函数,可以使用 override
关键字:
class Derived : public Base {
public:
void func() override {
std::cout << "Derived function" << std::endl;
}
};
在这个例子中,Derived
类的 func
函数明确标记为重写了 Base
类的 func
函数。如果派生类中的函数签名与基类不匹配,编译器会报错。例如:
class Derived : public Base {
public:
void func(int x) override { // 错误:签名与基类不匹配
std::cout << "Derived function" << std::endl;
}
};
这个错误会被及时发现,因为 func(int x)
的签名与基类的 func()
不匹配,编译器会指出这是不合法的重写。
关键点总结
override
用于确保虚函数的正确重写,避免签名不匹配问题。- 它能提高代码的可读性和可维护性,便于他人理解代码中的继承关系。
下面通过一个具体的代码实例展示了 override
的使用,以及它在派生类中防止函数签名不匹配和提高代码可读性等方面的作用。
示例代码
#include <iostream>
// 基类
class Animal {
public:
// 基类中的虚函数
virtual void makeSound() const {
std::cout << "Animal makes a sound." << std::endl;
}
// 基类中的虚函数
virtual void move(int distance) const {
std::cout << "Animal moves " << distance << " meters." << std::endl;
}
};
// 派生类
class Dog : public Animal {
public:
// 正确重写基类中的 makeSound 函数
void makeSound() const override {
std::cout << "Dog barks." << std::endl;
}
// 错误重写:这里 move 函数的参数类型不匹配
// void move(double distance) const override { // 如果这样写,编译器会报错,因为参数类型与基类不匹配
// std::cout << "Dog runs " << distance << " meters." << std::endl;
// }
// 正确重写基类中的 move 函数
void move(int distance) const override {
std::cout << "Dog runs " << distance << " meters." << std::endl;
}
// 自己定义的新函数,不重写基类
void wagTail() const {
std::cout << "Dog wags its tail." << std::endl;
}
};
int main() {
Animal* animal = new Animal();
Animal* dog = new Dog();
// 调用基类的虚函数
animal->makeSound(); // 输出: Animal makes a sound.
dog->makeSound(); // 输出: Dog barks. (派生类重写的版本)
animal->move(10); // 输出: Animal moves 10 meters.
dog->move(20); // 输出: Dog runs 20 meters. (派生类重写的版本)
// 直接调用派生类的非虚函数
// animal->wagTail(); // 错误,Animal 没有定义 wagTail
static_cast<Dog*>(dog)->wagTail(); // 输出: Dog wags its tail.
delete animal;
delete dog;
return 0;
}
代码解释
-
基类
Animal
:- 基类
Animal
定义了两个虚函数makeSound()
和move(int distance)
,它们可以被派生类重写。
- 基类
-
派生类
Dog
:Dog
类通过override
关键字正确重写了基类的虚函数makeSound()
和move(int distance)
。- 如果尝试使用不匹配的参数类型(例如
move(double distance)
),编译器会报错,这就是override
提供的安全性保证。
-
虚函数调用:
- 在
main()
函数中,基类指针animal
和派生类指针dog
指向了不同的对象。 - 当调用虚函数
makeSound()
和move()
时,dog
对象调用的是Dog
类中重写的函数,显示了狗的行为,而不是基类的默认行为。
- 在
-
派生类自定义函数:
Dog
类还定义了自己的函数wagTail()
,它不是重写基类中的函数,无法通过基类指针调用。
编译器报错示例
如果 Dog
类中的 move
函数定义为:
void move(double distance) const override { // 错误:函数签名不匹配 std::cout << "Dog runs " << distance << " meters." << std::endl; }
编译器会报错,因为基类中的 move
函数参数是 int
类型,而这里的参数是 double
类型。使用 override
关键字后,编译器会检查重写是否正确,从而帮助程序员发现这些潜在的错误。
总结,通过这个代码实例可以看到,override
能确保派生类中的函数与基类虚函数签名匹配,防止因疏忽引发的重写错误,并且提高代码的可读性和安全性。