C++ override specifier (C++ override 说明符)

时间:2024-07-11 18:54:20

C++ override specifier [C++ override 说明符]

  • 1. C++ override specifier
  • 2. C++ override 说明符
  • 3. Compiler Error C2723
  • 4. Managing Virtual Methods: `override` and `final` (管理虚方法 `override` 和 `final`)
  • References

在派生类中用 override 修饰符定义虚函数时,编译器会验证直接或间接基类是否包含特征标相同的虚函数。如果不包含,就生成一个错误消息。

1. C++ override specifier

C++ documentation (C++ 文档)
https://learn.microsoft.com/en-us/cpp/cpp/
https://learn.microsoft.com/zh-cn/cpp/cpp/

override specifier (override 说明符)
https://learn.microsoft.com/en-us/cpp/cpp/override-specifier
https://learn.microsoft.com/zh-cn/cpp/cpp/override-specifier

You can use the override specifier to designate member functions that override a virtual function in a base class.
可以使用 override 说明符来指定覆盖基类中的虚成员函数。

override is context-sensitive and has special meaning only when it’s used after a member function declaration; otherwise, it’s not a reserved keyword.
override 是上下文相关的,只有在成员函数声明时使用才具有特殊含义;否则,它不是保留关键字。

Use override to help prevent inadvertent inheritance behavior in your code. The following example shows where, without using override, the member function behavior of the derived class may not have been intended. The compiler doesn’t emit any errors for this code.
使用 override 有助于防止你的代码中出现意外的继承行为。以下示例演示在未使用 override 的情况下,可能不打算使用派生类的成员函数行为。编译器不会发出此代码的任何错误。

class BaseClass {
	virtual void funcA();
	virtual void funcB() const;
	virtual void funcC(int = 0);
	void funcD();
};

class DerivedClass : public BaseClass {
	virtual void funcA();  // Works as intended

	// DerivedClass::funcB() is non-const, so it does not override BaseClass::funcB() const 
	// and it is a new member function
	virtual void funcB();

	// DerivedClass::funcC(double) has a different parameter type than BaseClass::funcC(int), 
	// so DerivedClass::funcC(double) is a new member function
	virtual void funcC(double = 0.0);
};

When you use override, the compiler generates errors instead of silently creating new member functions.
当使用 override 时,编译器会生成错误,而不会在不提示的情况下创建新的成员函数。

class BaseClass {
	virtual void funcA();
	virtual void funcB() const;
	virtual void funcC(int = 0);
	void funcD();
};

class DerivedClass : public BaseClass {
	virtual void funcA() override; // ok

	// Compiler error: DerivedClass::funcB() does not override BaseClass::funcB() const
	virtual void funcB() override;

	// Compiler error: DerivedClass::funcC(double) does not override BaseClass::funcC(int)
	virtual void funcC(double = 0.0) override;

	// Compiler error: DerivedClass::funcD() does not override the non-virtual BaseClass::funcD()
	void funcD() override;
};

To specify that functions cannot be overridden and that classes cannot be inherited, use the final specifier.
若要指定不能重写函数且不能继承类,请使用 final 说明符。

2. C++ override 说明符

派生类如果定义了一个函数与基类中虚函数的名字相同但是形参列表不同,这仍然是合法的行为。编译器将认为新定义的这个函数与基类中原有的函数是相互独立的,派生类的函数并没有覆盖掉基类中的版本。就实际的编程习惯而言,这种声明往往意味着发生了错误,因为我们可能原本希望派生类能覆盖掉基类中的虚函数,但是一不小心把形参列表弄错了。

在 C++11 新标准中我们可以使用 override 修饰符来说明派生类中的虚函数。如果我们使用 override 标记了某个函数,但该函数并没有覆盖已存在的虚函数,此时编译器将报错。

struct B {
	virtual void f1(int) const;
	virtual void f2();
	void f3();
};

struct D1 : B {
	void f1(int) const override;  // 正确:f1 与基类中的 f1 匹配
	void f2(int) override;  // 错误:B 没有 void f2(int) 形式的函数
	void f3() override;  // 错误:f3 不是虚函数
	void f4() override;  // 错误:B 没有名为 f4 的函数
};

D1 中,f1override 说明符是正确的,因为基类和派生类中的 f1 都是 const 成员,并且它们都接受一个 int 并返回 void,所以 D1 中的 f1 正确地覆盖了它从 B 中继承而来的虚函数。

D1f2 的声明与 Bf2 的声明不匹配,显然 B 中定义的 f2 不接受任何参数而 D1f2 接受一个 int。因为这两个声明不匹配,所以 D1f2 不能覆盖 Bf2,它是一个新函数,仅仅是名字恰好与原来的函数一样而已。因为我们使用 override 所表达的意思是我们希望能覆盖基类中的虚函数而实际上并未做到,所以编译器会报错。

因为只有虚函数才能被覆盖,所以编译器会拒绝 D1f3。该函数不是 B 中的虚函数,因此它不能被覆盖。类似的,f4 的声明也会发生错误,因为 B 中根本就没有名为 f4 的函数。

3. Compiler Error C2723

‘function’ : ‘specifier’ specifier illegal on function definition

error C2723: '***': 'override' specifier illegal on function definition

'override' specifier is not allowed outside a class definition

The specifier cannot appear with a function definition outside of a class declaration. The override specifier can be specified only on a member function declaration within a class declaration.
说明符不能与类声明外部的函数定义同时出现,仅能对类声明内的成员函数声明指定 override 说明符。

4. Managing Virtual Methods: override and final (管理虚方法 overridefinal)

Virtual methods are an important component of implementing polymorphic class hierarchies, in which a base class reference or pointer can invoke the particular method appropriate to the type of object referred to. For instance, suppose the base class declares a particular virtual method, and you decide to provide a different version for a derived class. This is called overriding the old version. If you mismatch the function signature, you hide rather than override the old version.
虚方法对实现多态类层次结构很重要,让基类引用或指针能够根据指向的对象类型调用相应的方法,但虚方法也带来了一些编程陷阱。假设基类声明了一个虚方法,而你决定在派生类中提供不同的版本,这将覆盖旧版本。如果特征标不匹配,将隐藏而不是覆盖旧版本。

class Action {
	int a;

public:
	Action(int i = 0) : a(i) {}
	int Val() const { return a; };
	virtual void Func(char ch) const { std::cout << Val() << ch << "\n"; }
};

class Bingo : public Action {
public:
	Bingo(int i = 0) : Action(i) {}
	virtual void Func(char * ch) const { std::cout << Val() << ch << "!\n"; }
};

Because class Bingo uses Func(char * ch) instead of Func(char ch), Func(char ch) is hidden to a Bingo object.
由于类 Bingo 定义的是 Func(char * ch) 而不是 Func(char ch),将对 Bingo 对象隐藏 Func(char ch),这导致程序不能使用类似于下面的代码:

Bingo bingo(10);
bingo.Func('@');  // works for Action object, fails for Bingo object

With C++11, you can use the virtual specifier override to indicate that you intend to override a virtual function. Place it after the parameter list. If your declaration does not match a base method, the compiler objects.
在 C++11 中,可使用虚说明符 override 指出你要覆盖一个虚函数,将其放在参数列表后面。如果声明与基类方法不匹配,编译器将视为错误。

class Action {
	int a;

public:
	Action(int i = 0) : a(i) {}
	int Val() const { return a; };
	virtual void Func(char ch) const { std::cout << Val() << ch << "\n"; }
};

class Bingo : public Action {
public:
	Bingo(int i = 0) : Action(i) {}
	virtual void Func(char * ch) const override { std::cout << Val() << ch << "!\n"; }
};

A compile-time error message:

error C3668: 'Bingo::Func': method with override specifier 'override' did not override any base class methods

You may find that you want to prohibit derived classes from overriding a particular virtual method. To do so, place final after the parameter list. For example, the following code would prevent classes based on Action to redefine theFunc(char ch) function.
禁止派生类覆盖特定的虚方法,可在参数列表后面加上 final下面的代码禁止 Action 的派生类重新定义函数。

virtual void Func(char ch) const final { std::cout << Val() << ch << "\n"; }

The specifiers override and final do not quite have the status of keywords. Instead, they are labeled “identifiers with special meaning”. This means that the compiler uses the context in which they appear to decide if they have a special meaning. In other contexts, they can be used as ordinary identifiers (for example, as variable names or enumerations).
说明符 overridefinal 并非关键字,而是具有特殊含义的标识符。这意味着编译器根据上下文确定它们是否有特殊含义;在其他上下文中,可将它们用作常规标识符,如变量名或枚举。

References

[1] Yongqiang Cheng, https://yongqiang.blog.****.net/
[2] C++ final specifier 禁止派生类和禁止重写函数, https://blog.****.net/chengyq116/article/details/104477726
[3] C++ Primer Plus, 6th Edition, https://www.informit.com/store/c-plus-plus-primer-plus-9780321776402

  • Managing Virtual Methods: override and final