30多年来,C++一直没有继承控制关键字。最起码这是不容易的,禁止一个类的进一步衍生是可能的但也很棘手。为避免用户在派生类中重载一个虚函数,你不得不向后考虑。

C++ 11添加了两个继承控制关键字:override和final

override:确保在派生类中声明的重载函数跟基类的虚函数有相同的签名。

final:阻止类的进一步派生和虚函数的进一步重载。接下来让我们看看这些监督者如何消除你在类层次结构的设计和实施中的bug吧。

 

虚函数重载

一个派生类可以重载基类中声明的成员函数,这是面向对象设计的基础。然而像重载一个函数这么简单的操作也会出错。关于重载虚函数的两个常见错误如下:

  • 无意中重载
  • 签名不匹配

首先,我们来分析一下无意中重载的综合症。你可能只是通过声明一个与基类的某个虚成员函数具有相同的名字和签名的成员函数而无意中重载了这个虚函数。编译器和读代码的人很难发现这个bug因为他们通常以为这个新函数是为了实现对基类函数的重载:

struct A
{
virtual void func();
};
struct B: A{};
struct F{};
struct D: A, F
{
void func(); //定义一个新函数,不小心重写了基类的虚函数A::func
};

阅读以上代码,你不能确定成员函数D::func()是否故意重载了A::func().它也可能是个偶然发生的重载,因为两个函数的参数列表和名字都碰巧一样。

签名不匹配是一个更为常见的情景。这导致意外创建一个新的虚函数(而不是重载一个已存在的虚函数),正如以下例子所示:

struct G
{
virtual void func(int);
};
struct H: G
{
virtual void func(double); //不小心创建了一个新的虚函数
};

这种情况下,程序员本打算在类H中重载G::func()的。然而,由于H::func()拥有不同的签名,结果创建了一个新的虚函数,而非对基类函数的重载:

H *p=new H;
p->func();    // calls G::f
p->func(5.0); // calls H::f

碰到这种情况,不是所有的编译器都会给个警告,有时那样做会被设置成抑制这种警告。在C++11中,通过使用新关键字override可以消除这两个bugs。

override明确地表示一个函数是对基类中一个虚函数的重载,它会检查基类虚函数和派生类中重载函数的签名,如果签名不匹配,编译器会发出错误信息。

当处理到H::func()声明时,编译器会 在一个基类查找与之匹配的虚函数。“匹配”在上下文中的意思是:

  • 相同的函数名
  • 在声明该函数的第一个基类中的一个虚拟说明符
  • 基类函数和派生类重载函数具有相同的参数列表、返回类型(一种情况例外)、cv资格等。

如果这三个条件中任意一个不满足,编译器就会报错(在我们的例子中:G::func()需要int型而H::func()要求double的)。要是没有关键字override,编译器会简单的认为程序员想要在H中新建一个虚函数。

如果派生类函数确实是要重载基类的一个函数,它应该包含一个显式的override说明符。否则,假定D::func()是一个新的虚函数(这时,加一个注释是值得赞赏的),或者这也许是个bug。

 

final函数和类

C++11的关键字final有两个用途:

(1).它阻止了从类继承。

(2).阻止一个虚函数的重载。

某些实现系统服务、基础功能和加密等的类通常是不允许有子类的;实现者不想客户端从这些类派生新类而修改他们。标准库容器,如std:: vector和std:: list的无子类化类型就是另一个很好的例子,这些容器没有虚拟析构函数或者确切地说没有任何虚成员函数。

然而,程序员常常在没有意识到风险的情况下坚持从std::vector派生。在C++11中,无子类类型将被声明为如下所示:

class TaskManager {/*..*/} final;
class PrioritizedTaskManager: public TaskManager {
}; //compilation error: base class TaskManager is final

同样,你可以通过声明它为final来禁止一个虚函数被进一步重载。如果一个派生类试图重载一个final函数,编译器就会报错:

struct A
{
virtual void func() const;
};
struct B: A
{
void func() const override final; //OK
};
struct C: B
{
void func()const; //error, B::func is final
};
C::func()是否声明为override没关系,一旦一个虚函数被声明为final,派生类不能再重载它。
 

语法和术语

迄今为止,我已经避免了两个有关override和final的次要问题。第一个是它们独特的位置。与virtual、inline、explicit extern以及一些类似的函数说明符不同的是,这两个关键字放在函数参数列表右括号之后,或者(对于无子类的类来说)一个类声明的右大括号之后。

这些关键字的特殊位置是由另一个不同寻常的性质决定的:override和final不是普通关键字,事实上官方地说,它们根本不是关键字。C++11把它们作为只是为了在特定上下文和位置下获取特殊意义的标示符。在任何其他位置或上下文,它们都被当成标示符。因此,一个完全有效的C++11代码如下:

//valid C++11 code
int final = ;
bool override = false;
if (override==true){
cout<<"override is:"<<override<<endl;}
struct D{} final;
struct A{ virtual bool func(); };
struct B:A { bool func() override final; };

这似乎有点不可思议,final和override酷似PL/ 1的上下文敏感关键词(CSK)。自1972年以来,C和后来的C+ +一直都很抵触CSK坚持保留关键字的做法。

 

那为什么委员会将final和override另外处理呢?选择CSK只是一种妥协方案。将override和final作为保留关键字可能对现有C++代码造成破坏。如果委员会已经引入了新的保留关键字,他们可能会选择像final_declor_Override这样时髦的,且不太可能与传统C++代码中用户声明的标示符相冲突的字符串等。然而,没有谁喜欢这么丑的关键字(比如,问问C使用者对C99的s_Bool的看法)。这是为什么最终采用CSK方法的原因。

override和final在C++11中作为关键字,但只在特定的上下文使用,不然它们只被当成普通标示符。

C++11 override和final的更多相关文章

  1. [转][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 ...

  2. C++11 override 和 final 关键字

    C++11之前,一直没有继承控制关键字.禁用一个类的进一步衍生是可能的但也很棘手.为避免用户在派生类中重载一个虚函数,你不得不向后考虑. C++ 11添加了两个继承控制关键字:override和fin ...

  3. c++ 11 override final

    C++ 11添加了两个继承控制关键字:override和final. override确保在派生类中声明的重载函数跟基类的虚函数有相同的签名.final阻止类的进一步派生和虚函数的进一步重载

  4. C++11学习笔记(5) —— override and final (转)

    原文转自 http://blog.csdn.net/fire_lord/article/details/8540592 1.简介 C++为我们提供了继承和虚函数的重写特性. 在派生类中,重写虚函数不强 ...

  5. C++11之重写说明符override和final

    关于 本文代码演示环境: win10 + vs2017 一个困扰 之前MFC用的多了,发现一个问题: 子类窗口的某个函数是否重载了基类的函数.解决办法是: 打开基类的代码,一个个排查. 这只是一个具体 ...

  6. Cannot override the final method from SherlockFragmentActivity

    调用ActionBarSherlock后页面找不到onCreateOptionsMenu报错 com.actionbarsherlock.app.SherlockFragmentActivity.on ...

  7. Effective Java 11 Override clone judiciously

    Principles If you override the clone method in a nonfinal class, you should return an object obtaine ...

  8. override与final

    override 强调该函数是重写的父类的函数 final 指定该函数不能被重写 两者都是针对virtual 函数

  9. 抽象类,override,final和类模板

    抽象类: **有些函数由于信息不够具体,而无法实现** 由此而来的纯虚函数:在基类中声明的纯虚函数,在基类中无法实现(是因为在基类中定义的信息不够具体,不是学的知识不够),于是这个函数没办法规定具体的 ...

随机推荐

  1. Openwrt中用iftop查看网络流量情况

    iftop可以查看指定网卡上的流量情况, 命令说明 iftop: display bandwidth usage on an interface by host Synopsis: iftop -h ...

  2. SSD 为什么顺序写比随机写性能更好?

    SSD以Page为单位做读写,以Block为单位做垃圾回收,Page一般有16KB大小,Block一般有几十MB大小,SSD写数据的逻辑是: 1)将该块数据所在的Page读出 2)修改该Page中该块 ...

  3. Golang 用go-sql-driver 调用MySQL存储过程时的问题排查

    今天在用go-sql-driver做MySQL driver调用MySQL的存储过程时遇到一个罕见的报错.本文对这个错误及其解做一个简单的总结. 1 问题描述 按照go中sql包提供的接口,应用层代码 ...

  4. [转]nonlocal和global

    在Python中,当引用一个变量的时候,对这个变量的搜索是按找本地作用域(Local).嵌套作用域(Enclosing function locals).全局作用域(Global).内置作用域(bui ...

  5. 查看/修改Linux时区和时间

    一.时区 1. 查看当前时区 date -R 2. 修改设置时区 方法(1) tzselect 方法(2) 仅限于RedHat Linux 和 CentOS timeconfig 方法(3) 适用于D ...

  6. (转)Groupon前传:从10个月的失败作品修改,1个月找到成功 并不挶泥在这个点子上面,它反而往后站一步,看看他们已经做好的这个网站,可以再怎么包装成另一个完完全全不同的网站?所有的人所做的每件失败的事情中, 一定有碰到或含有成功的答案」在里面,只是他们不知道而已。 人不怕失败」,只怕宣布失败」

    (转)Groupon前传:从10个月的失败作品修改,1个月找到成功 今天读到 一个非常励志人心的故事 ,就像现在「叶问」有「前传」,最近很火红的团集购网站Groupon 也出现了「Groupon前传」 ...

  7. HDU 1023 Train Problem II (大数卡特兰数)

    Train Problem II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  8. MySQL 插入emoji 表情

    create table doctorUserInfoMation ( id int not null auto_increment comment '系统ID', userId ) comment ...

  9. openstack rpc机制

    一.概述: 在openstack项目中,api的调用规则: 跨项目:如nova调用keystone, glance,cinder等,使用rest api(通过相应的python-XXXclient 库 ...

  10. Kafka生产环境中的错误

    最近在处理日志收集任务时,发现前端服务器用flume进行收集,逐步把所有的服务器都增加上.增加的差不多时.Kafka报了如下类似错误: 709 [main] WARN kafka.producer.a ...