[转][C++ 11]override and final - write clean and maintainable C++ code

原文:

http://arne-mertz.de/2015/12/modern-c-features-override-and-final/

Today I write about a pair of less often discussed, less complicated features introduced in C++11, which are nevertheless useful. Both can provide some additional security and clarity when it comes to deriving classes and overloading virtual functions.

Overriding virtual methods

Have you ever come across the problem that you overloaded a virtual function in a derived class but it did not get called? Or worse – you had to change the signature of the base class virtual function. Searching for all the derived classes that overloaded the function can be tricky, and Murphy’s law states that you forget at least one of them.

 1 struct Base {
 2   virtual void doSomething(int i) const {
 3     std::cout << "Base::doSomething with " << i << ‘\n‘;
 4   }
 5 };
 6
 7 struct Derived : Base {
 8   virtual void doSomething(int i) {
 9     std::cout << "Derived::doSomething with " << i << ‘\n‘;
10   }
11 };
12
13 void callIt(Base& b) {
14   b.doSomething(42);
15 }
16
17 int main() {
18   Derived d;
19   callIt(d); //OOPS: "Base::doSomething with 42"
20 }

Trust me, I have spent hours looking for errors like this. If you have not spotted it yet: Derived::doSomething is missing the const specifier. Therefore it does not have the same signature and is not overloading Base::doSomething, period. There are compilers out there emitting warnings for that kind of stuff, but those warnings appear also if we did in fact not want to overload the virtual function.

For cases like this, we’d like to have the tools to distinguish between accidents where the compiler preferably should emit an error and intent, where it should remain silent. Therefore, C++11 introduced the keyword override:

1 struct Derived : public Base {
2   void doSomething(int i) override { //ERROR: does not override Base::doSomething
3     std::cout << "Derived::doSomething with " << i << ‘\n‘;
4   }
5 };

It’s as easy as this. Add the keyword, and the compiler checks if this method in fact is overriding a base class method. Thus the aforementioned change of the function signature in the base class will lead to compiler errors in every derived class’ method that declares to be overriding but isn’t until their signature is changed, too.

Override brings an additional benefit if you apply it consistently: Previous to C++11 it was a debatable question of style if overriding functions in derived classes should be marked virtual as well or not. Since functions that override virtual functions are automatically virtual as well, it was not necessary, but explicitly stating that the function should be virtual documented that fact. With override, the documentation is already in place and virtual is only needed for the topmost virtual functions.

Preventing virtual function overrides

The almost exact opposite case is when you define virtual functions in base classes but don’t want deriving classes to override them. This can be the case when you design the top layers of class hierarchies that are designed to be extended by deriving classes. A crucial point is that virtual functions can be overridden even if the base class function is private:

 1 //---- mylib.h ----------------------
 2 class AbstractLibraryBase {
 3 public:
 4   void templateMethod() const {
 5     std::cout << "Something about " << implDetail() << ‘\n‘;
 6   }
 7 private:
 8   virtual int implDetail() const = 0;
 9 };
10
11 class LibraryClass : public AbstractLibraryBase {
12 private:
13   int implDetail() const override {
14     return 42;
15   }
16 };
 1 #include <mylib.h>
 2 class EvilHijacker : public LibraryClass {
 3   int implDetail() const override {
 4     return 73; //overriding the private method
 5   }
 6 };
 7
 8 int main() {
 9   EvilHijacker eh;
10   eh.templateMethod(); //Something about 73
11 }

Until C++11 there was little you could do to prevent such things. Workarounds had to be used to further separate those private virtual methods from derived classes and prevent the hijack. Now we have the keyword final to the rescue:

1 class LibraryClass : public AbstractLibraryBase {
2 private:
3   int implDetail() const final override {
4     return 42;
5   }
6 };
1 class EvilHijacker : public LibraryClass {
2   int implDetail() const override; //ERROR: overriding final function...
3 };

Now it is impossible to further override implDetail in classes that derive from LibraryClass. It is, of course, possible to derive more classes from AbstractLibraryBase that can (and in this case have to) override the function.

A quick note on the positioning of both final and override:  both have to be positioned after const, volatile and reference specifiers, but before the pure specifier, i.e. the =0, if the function should have one. A pure and final function does not make sense since it makes the class abstract and no derived class can fix it, but there can be use cases for pure virtual overrides. It makes no difference if you write override final or final override. However, I prefer the latter as it reads more fluently.

Final classes

There is a second use for final: applied to a class definition directly after the class name, it prohibits any other class to derive from the class in question, no matter if it wants to inherit publicly or privately:

1 class NoDerivates final  /* : BaseClasses if needed */ {
2   // ...
3 };
4
5 class Fail : public NoDerivates { //ERROR: canot derive from final base
6 };

Before using final for classes or methods consider if you really need to be

Updating your codebase

If you have an existing codebase it can be tedious to try to update all virtual functions with final and override. The decision to mark a function final needs to be decided from case to case, whereas adding the override specifier is straight forward. Whether you want to tackle the task and add the specifiers all at once or just fix those places you have to deal with anyways, here is a simple recipe:

Add the override specifier to every function of a class, virtual or not and recompile the class. The compiler will immediately complain about functions that are not overriding a virtual base class method. Remove the overrides that cause compiler errors and then remove the virtual specifier of any function that has an override specifier.

When you find a function that is declared virtual, you won’t always know immediately if it is the topmost virtual function or not, and finding all the overriding functions manually is hard. Luckily you can get help from your compiler. Temporarily mark the function in question final and recompile the project. The compiler will give you a list of all overriding functions in form of “cannot override final” errors.

Conclusion

Both override and final can help to avoid errors related to virtual functions. While final needs a bit of thought about when it should be applied and when not, the use of override is straight forward and there is no excuse to leave it away.

时间: 2024-10-15 01:46:47

[转][C++ 11]override and final - write clean and maintainable C++ code的相关文章

c++ 11 override final

C++ 11添加了两个继承控制关键字:override和final. override确保在派生类中声明的重载函数跟基类的虚函数有相同的签名.final阻止类的进一步派生和虚函数的进一步重载 原文地址:https://www.cnblogs.com/zhangdongsheng/p/8302205.html

[The Diary] 11.9 The Final Day

11.9 The Final Day--终究还是来到了这一天. 今天下午,我们去爬的山,一路上,我们欢声笑语,同时对NOIP充满着强烈的信心.我和rjj,clz谈话,谈笑风生(雾). 或许,这就是最终章了吧....我已无法用言语来表达我此时的心情了,毕竟,人总有悲欢离合吧.... 太长的故事,说不完...我不太会用文字表述心情,但是,一年了,我已对这机房产生了感情,它是我唯一能够静下心来去钻研一个东西,去认真学习的一个地方,可能有些人觉得,我在文化课方面用功夫太少,导致我暑假考试崩盘,但是,在崩

安装了VS2010 sp1 后再安装ASP.NET MVC 3.0的问题(Final Result: Installation failed with error code: (0x80070643), &quot;安装时发生严重错误 &quot; (Ela)

原文:安装了VS2010 sp1 后再安装ASP.NET MVC 3.0的问题(Final Result: Installation failed with error code: (0x80070643), "安装时发生严重错误 " (Ela) 安装了VS2010 sp1 后再安装ASP.NET MVC 3.0的问题(Final Result: Installation failed with error code: (0x80070643), "安装时发生严重错误 &qu

Effective Java 英文 第二版 读书笔记 Item 11:Override clone judiciously

x.clone()!=x will be true x.clone().getClass()==x.getClass() will be true x.clone().equals(x) always true. 意味着深复制,新建对象,数据与结构与原对象一致, Copying an object will typically entail creating a new instance of tis class,but it may require copying of internal da

c++中override和final

1.override保留字表示当前函数重写了基类的虚函数. 目的:1.在函数比较多的情况下可以提示读者某个函数重写了基类虚函数(表示这个虚函数是从基类继承,不是派生类自己定义的):2.强制编译器检查某个函数是否重写基类虚函数,如果没有则报错. 用法:在类的成员函数参数列表后面添加该关键字既可,被override修饰后如果父类无对应的虚函数则报错,无法override. 例子:class Base {virtual void f();}: class Derived : public Base {

C++的override和final

1.final用于让虚函数不可被重写 struct B2 { virtual void f() final {} // final 函数 }; struct D2 : B2 { virtual void f() {} }; 如上代码是不可被编译过的 2.override 1.在函数比较多的情况下可以提示读者某个函数重写了基类虚函数(表示这个虚函数是从基类继承,不是派生类自己定义的): 2.强制编译器检查某个函数是否重写基类虚函数,如果没有则报错. 原文地址:https://www.cnblogs

Android应用程序模拟手机按键

记得以前在做一个C++项目时,需要在某一步操作之后人为用代码模拟敲键盘上的回车键(Enter)效果. 出于好奇,这几天研究了一下Android中手机(或平板)上各种按键的键值.模拟方法及最终效果. 1.先来看看Android中对按键和值的定义方式: 1 public static final int KEYCODE_UNKNOWN = 0; 2 /** Key code constant: Soft Left key. */ 3 public static final int KEYCODE_S

Android上实现MVP模式的途径

今天我想分享我在Android上实现MVP(Model-View-Presenter)模式的方法.如果你对MVP模式还不熟悉,或者不了解为什么要在Android应用中使用MVP模式,推荐你先阅读这篇维基百科文章和这篇博客. 使用Activity和Fragment作为View合适么? 目前,在很多使用了MVP模式的Android项目中,主流做法是将Activity和Fragment作为视图层来进行处理.而Presenters通常是通过继承被视图层实例化或者注入的对象来得到的.我认可这种方式可以节省

App(2)

@Entity public class BBill{ @Id(autoincrement = true) private Long id; //本地id private String rid; //服务器端id private float cost; //金额 private String content; //内容 private String userid; //用户id private String payName; //支付方式 private String payImg; // pr