[转][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.
struct Base {
virtual void doSomething(int i) const {
std::cout << "Base::doSomething with " << i << '\n';
}
}; struct Derived : Base {
virtual void doSomething(int i) {
std::cout << "Derived::doSomething with " << i << '\n';
}
}; void callIt(Base& b) {
b.doSomething();
} int main() {
Derived d;
callIt(d); //OOPS: "Base::doSomething with 42"
}
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
:
struct Derived : public Base {
void doSomething(int i) override { //ERROR: does not override Base::doSomething
std::cout << "Derived::doSomething with " << i << '\n';
}
};
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:
//---- mylib.h ----------------------
class AbstractLibraryBase {
public:
void templateMethod() const {
std::cout << "Something about " << implDetail() << '\n';
}
private:
virtual int implDetail() const = ;
}; class LibraryClass : public AbstractLibraryBase {
private:
int implDetail() const override {
return ;
}
};
#include <mylib.h>
class EvilHijacker : public LibraryClass {
int implDetail() const override {
return ; //overriding the private method
}
}; int main() {
EvilHijacker eh;
eh.templateMethod(); //Something about 73
}
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:
class LibraryClass : public AbstractLibraryBase {
private:
int implDetail() const final override {
return ;
}
};
class EvilHijacker : public LibraryClass {
int implDetail() const override; //ERROR: overriding final function...
};
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:
class NoDerivates final /* : BaseClasses if needed */ {
// ...
}; class Fail : public NoDerivates { //ERROR: canot derive from final base
};
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.
[转][C++ 11]override and final - write clean and maintainable C++ code的更多相关文章
- C++11 override 和 final 关键字
C++11之前,一直没有继承控制关键字.禁用一个类的进一步衍生是可能的但也很棘手.为避免用户在派生类中重载一个虚函数,你不得不向后考虑. C++ 11添加了两个继承控制关键字:override和fin ...
- C++11 override和final
30多年来,C++一直没有继承控制关键字.最起码这是不容易的,禁止一个类的进一步衍生是可能的但也很棘手.为避免用户在派生类中重载一个虚函数,你不得不向后考虑. C++ 11添加了两个继承控制关键字:o ...
- c++ 11 override final
C++ 11添加了两个继承控制关键字:override和final. override确保在派生类中声明的重载函数跟基类的虚函数有相同的签名.final阻止类的进一步派生和虚函数的进一步重载
- C++11学习笔记(5) —— override and final (转)
原文转自 http://blog.csdn.net/fire_lord/article/details/8540592 1.简介 C++为我们提供了继承和虚函数的重写特性. 在派生类中,重写虚函数不强 ...
- C++11之重写说明符override和final
关于 本文代码演示环境: win10 + vs2017 一个困扰 之前MFC用的多了,发现一个问题: 子类窗口的某个函数是否重载了基类的函数.解决办法是: 打开基类的代码,一个个排查. 这只是一个具体 ...
- Cannot override the final method from SherlockFragmentActivity
调用ActionBarSherlock后页面找不到onCreateOptionsMenu报错 com.actionbarsherlock.app.SherlockFragmentActivity.on ...
- 安装了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: (0x800706 ...
- Effective Java 11 Override clone judiciously
Principles If you override the clone method in a nonfinal class, you should return an object obtaine ...
- override与final
override 强调该函数是重写的父类的函数 final 指定该函数不能被重写 两者都是针对virtual 函数
随机推荐
- 第十章:鸟哥的Linux私房菜
第十章.vim程式编辑器 1. vi与vim 1.1 为何要学vim2. vi的使用 2.1 简易执行范例 2.2 按键说明 2.3 一个案例的练习 2.4 vim的暂存档.救援回复与开启时的警告讯息 ...
- thinkphp开发技巧经验分享
thinkphp开发技巧经验分享 www.111cn.net 编辑:flyfox 来源:转载 这里我给大家总结一个朋友学习thinkphp时的一些笔记了,从变量到内置模板引擎及系统变量等等的笔记了,同 ...
- DirectX 基础学习系列6 字体
DIRECTX9自带ID3DXFONT类 内部调用GDI的接口,效率一般,但能够处理一些复杂的字体 HRESULT D3DXCreateFontIndirect( LPDIRECT3DDEVICE9 ...
- javascript Math.pow 函数 详解
语法 Math.pow(x,y) 定义和用法 pow() 方法可返回 x 的 y 次幂的值. 处理简单数学问题 6的4次方等于1296,记作:64=1296; 求值: Math.pow(6,4)=12 ...
- 20145235 《Java程序设计》第4周学习总结
代码托管截图 教材学习内容总结 继承 •继承:继承基本上就是避免多个类间重复定义共同行为. 我理解的就是:在编写程序的过程中可能会出现部分代码重复的现象,把重复的部分单独定义为一类(父类),在其他代码 ...
- mysql-insert-返回主键id
function gen_this_insert_id($insert) { GLOBAL $link; $insert .= ' SELECT LAST_INSERT_ID();'; if (mys ...
- 汇总10.4版本ArcGIS Server与ArcMap 安装+SDE+注册数据源(非破解)
文档参考了Server技术支持部各位前辈的总结文档. win10 + Server 10.4 + ArcMap 10.4 + Oracle instant client (32位 和 64位) 安装 ...
- JS初学者必备的几个经典案例(一)!!!
一:选中复选框按钮可用 和 倒计时10秒后按钮可用 这是倒计时10秒后按钮可用 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...
- 分享一个开源的流程图绘制软件--Diagram Designer
最近在写专利文件,在制作说明书附图时想到自己还只会用wps进行简单的绘制,于是想学习下,填补下这方面的短板.这两天查到了DiagramDesigner这个小工具,派上了大用场.用它写完了一个发明专利, ...
- NSURLSession
参考文章1, apple文档 一.NSURLSessionConfiguration 介绍:分别配置每一个 session 对象.(NSURLConnection 很难做到) 分类: 1) defau ...