什么是函数签名?

函数签名(Signature)指的是函数名和参数(包括类型、个数、顺序)的组合。如果说两个函数的签名不同,那么它们就是两个函数。这里需要注意的是:返回值类型并不属于函数签名,因为 C++ 调用一个函数是可以忽略其返回值的,这种情况下编译器就无法根据返回值类型来确定调用哪一个函数。

什么是函数重载?

函数重载其实就是复用了同一个函数名而已,重载的函数只是要求参数列表不同,可以是参数类型不同,参数个数不同,或参数顺序不同等。同上,重载也不能用返回值来区别。

什么是函数覆盖?

函数覆盖是子类和父类之间的关系,是垂直关系;而重载是同一个类中不同方法之间的关系,是水平关系;

  1. class Entity {
  2. public:
  3. Entity(){};
  4. void Func(int a)
  5. {
  6. std::cout << "Entity" << std::endl;
  7. }
  8. };
  9. class Person : public Entity {
  10. public:
  11. Person() {};
  12. void Func(int a)
  13. {
  14. std::cout << "Person" << std::endl;
  15. }
  16. };
  17. int main()
  18. {
  19. Entity* e = new Person();
  20. e->Func(0); // 输出 Entity
  21. std::cin.get();
  22. }

Entity 类和 Person 类里面定义了两个一模一样的函数 Func,这两个类之间虽然是继承关系,但是这两个函数之间没有任何关系,它们只是属于不同类里面的方法。

  1. #include <iostream>
  2. class Entity {
  3. public:
  4. Entity(){};
  5. virtual void Func(int a)
  6. {
  7. std::cout << "Entity" << std::endl;
  8. }
  9. };
  10. class Person : public Entity {
  11. public:
  12. Person() {};
  13. void Func(int a)
  14. {
  15. std::cout << "Person" << std::endl;
  16. }
  17. };
  18. int main()
  19. {
  20. Entity* e = new Person();
  21. e->Func(0); // 输出 Person
  22. std::cin.get();
  23. }

Entity 类和 Person 类里面定义了两个一模一样的函数 Func,唯一不同的是,基类中的函数被关键字 virtual 修饰了,很明显,这是一个虚函数。当定义一个指向派生类的基类对象的指针时,调用该函数时,实际指向的是派生类中的函数。

虚函数的两个常见错误

虚函数的两个常见错误:无意的重写、虚函数签名不匹配。

  • 无意的重写在派生类中声明了一个与基类的某个虚函数具有相同的签名的成员函数,不小心重写了这个虚函数。
  1. class Base {
  2. public:
  3. virtual void Show(); // 虚函数
  4. };
  5. class Derived : public Base {
  6. public:
  7. void Show(); // 无意的重写
  8. };
  • 虚函数签名不匹配的错误通常是因为 函数名、参数列表 或 const 属性不一样,导致意外创建了一个新的虚函数,而不是重写一个已存在的虚函数。编译器并不会报错,因为它不知道你的目的是重写虚函数,而是把它当成了新的虚函数。

针对上述情况,C++ 11 增加了两个继承控制关键字:override 和 final,两者的作用分别为:

  • override:保证在派生类中声明的重载函数,与基类的虚函数有相同的签名;

  • final:阻止类的进一步派生 和 虚函数的进一步重写。

override

比如下面的代码,加了override,明确表示派生类的这个虚函数是重写基类的,如果派生类与基类虚函数的签名不一致,编译器就会报错。

  1. class Base {
  2. public:
  3. virtual void Show(int x); // 虚函数
  4. };
  5. class Derived : public Base {
  6. public:
  7. virtual void Show(int x) const override; // const 属性不一样,新的虚函数
  8. };

final

如果不希望某个类被继承,或不希望某个虚函数被重写,则可以在类名和虚函数后加上 final 关键字,加上 final 关键字后,再被继承或重写,编译器就会报错。

  1. class Base {
  2. public:
  3. virtual void Show(int x) final; // 虚函数
  4. };
  5. class Derived : public Base {
  6. public:
  7. virtual void Show(int x) override; // 重写提示错误
  8. };