C++中关键字 override 的简析

目录

在C++中,虚函数是最常见的实现多态的机制之一,来个最简单的例子温习一下:

class Base // 基类
{
public:
    virtual void f(){cout << "Base::f()" << endl;}
};
​
class Derived1 : public Base // 派生类1
{
    virtual void f(){cout << "Derived1::f()" << endl;}
};
​
class Derived2 : public Base // 派生类2
{
    virtual void f(){cout << "Derived2::f()" << endl;}
};

以上是一个基类 Base 及其派生子类的最简示例,基类中有一个普通虚函数 f( ),并且派生类们都复写(即override)了该虚函数。

以上代码的含义再清楚不过:我们希望通过基类指针或者基类引用,可以调用派生类版本的函数 f( ),以此实现所谓的多态,如下代码所示:

Base *b;

b  = new Derived1;
b->f(); // 打印 "Derived1::f()"

b = new Derived2;
b->f(); // 打印 "Derived2::f()"

但,作为一名普通虚函数 f( ),它实际上并不要求我们一定要复写(即override)它,假如你在派生类中不复写它,那么派生类将很自然地使用基类所提供的备用版本。

危险就在于此,人类是一个有诸多毛病的物种,其中一个根深蒂固的毛病是自以为是和粗心大意,因此以下代码很有可能出自某个同胞之手:

class Derived3 : public Base // 派生类3
{
    // 注意:以下函数有参数
    // 人类以为复写了基类虚函数,但实际并没有
    virtual void f(int){cout << "Derived3::f()" << endl;}
};

很明显,这位同胞的本意与以上两个派生类相同:派生出Derived3,并复写虚函数 f( )。很可惜,如果此时这位同胞贸然执行如下代码,将带来灾难性的后果:

Base *b;
b = new Derived3;
b->f(); // 原想打印 "Derived2=3::f()"
        // 实际却打印"Base::f()"!

如果这不够灾难,可以将函数 f( ) 想象成民航飞机的起飞引导程序。

现在问题很明显了:

派生类的虚函数的复写,很有可能出现乌龙——人类自以为复写了基类的虚函数(比如 void f( )) ,但实际上却写了另一个函数(比如 void f(int)) ,要命的是C++语法并不制止这种愚蠢的行为,它会以为这是我们出于某种神秘的原因才这么干的。

然后,执行程序,就这。。

解决办法:

消除人类与编译器之间深刻的误会,即:我们在想复写虚函数的时候,也同时将此想法明明白白地告诉编译器,别让它有什么误会。怎么告诉它呢?蹬蹬噔噔憋了半天主角终于出场鸟:

class Derived3 : public Base // 派生类3
{
    // 注意:
    // 此处的 override 明明白白告诉编译器:我要复写虚函数
    // 但由于基类没有 void f(int),因此此处将报错!哦也!
    virtual void f(int) override
    {cout << "Derived3::f()" << endl;}
};

到此这篇关于C++中关键字 override 的简析的文章就介绍到这了,更多相关C++中关键字 override 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详谈c++11 final与override说明符

    如下所示: //final,override出现在形参列表以及尾置返回类型之后 #include <iostream> using namespace std; struct B{ virtual void f1(int) const; virtual void f2(int); void f3(int) final;//出错,final不能修饰非虚函数 }; struct D:B{ void f1(int) const override ;//去掉const将出错,必须和基类中的函数原型一致

  • C++ override关键字使用详解

    C++ override从字面意思上,是覆盖的意思,实际上在C++中它是覆盖了一个方法并且对其重写,从而达到不同的作用.在我们C++编程过程中,最熟悉的就是对接口方法的实现,在接口中一般只是对方法进行了声明,而我们在实现时,就需要实现接口声明的所有方法.还有一个典型应用就是在继承中也可能会在子类覆盖父类的方法. 公有继承包含两部分:一是"接口"(interface),二是 "实现" (implementation). 例如Person类的几种成员函数的继承方式: c

  • 详解C++成员函数的override和final说明符的用法

    override 说明符 可使用 override 关键字来指定在基类中重写虚函数的成员函数. 语法 function-declaration override; 备注 override 仅在成员函数声明之后使用时才是区分上下文的且具有特殊含义:否则,它不是保留的关键字. 使用 override 有助于防止您的代码中出现意外的继承行为.以下示例演示在未使用 override 的情况下,可能不打算使用派生类的成员函数行为.编译器不会发出此代码的任何错误. class BaseClass { vir

  • C++中overload,override,overwrite的区别详细解析

    Overload(重载):在C++程序中,可以将语义.功能相似的几个函数用同一个名字表示,但参数或返回值不同(包括类型.顺序不同),即函数重载.(1)相同的范围(在同一个类中):(2)函数名字相同:(3)参数不同:(4)virtual 关键字可有可无. Override(覆盖):是指派生类函数覆盖基类函数,特征是:(1)不同的范围(分别位于派生类与基类):(2)函数名字相同:(3)参数相同:(4)基类函数必须有virtual 关键字. Overwrite(重写):是指派生类的函数屏蔽了与其同名的

  • C++中Overload,Override,Hide之间的区别

    一. 简介•Overload: 重载,指函数同名,但是参数个数不同.或者参数类型不同的多个实现.(如果参数相同但是仅仅返回值不同不是重载,编译器会报错.) 编译器判断重载函数:•第一步,是确定该调用中所考虑的重载函数的集合,该函数集合被称为候选函数(candidant function).所谓候选函数就是与被调用函数同名的函数. •第二步,分为两动作:第一个动作是编译器从第一步选出的候选函数中调出可行函数(viable function).可行函数的函数参数个数与调用的函数参数个数相同,或者可行

  • C++中关键字 override 的简析

    目录 在C++中,虚函数是最常见的实现多态的机制之一,来个最简单的例子温习一下: class Base // 基类 { public: virtual void f(){cout << "Base::f()" << endl;} }; ​ class Derived1 : public Base // 派生类1 { virtual void f(){cout << "Derived1::f()" << endl;} }

  • Android开发中多进程共享数据简析

    背景 最近在工作中遇到一个需求,需要在接收到推送的时候将推送获得的数据存起来,以供app启动时使用.我们会认为这不是So easy吗?只要把数据存到SharedPreferences中,然后让app打开同一个SharedPreferences读取数据就可以了.但是在实际的测试中,我们发现推送进程存入的数据,并不能在app进程中获得.所以这是为什么呢,也许聪明的读者从我们上面的陈述中已经发现了原因,因为我们有两个进程,推送进程负责将推送数据存入,而app进程负责读取,但是正是由于是两个进程,如果它

  • java多线程中的异常处理机制简析

    在java多线程程序中,所有线程都不允许抛出未捕获的checked exception,也就是说各个线程需要自己把自己的checked exception处理掉.这一点是通过java.lang.Runnable.run()方法声明(因为此方法声明上没有throw exception部分)进行了约束.但是线程依然有可能抛出unchecked exception,当此类异常跑抛出时,线程就会终结,而对于主线程和其他线程完全不受影响,且完全感知不到某个线程抛出的异常(也是说完全无法catch到这个异常

  • Javascript在IE和FireFox中的不同表现简析

    1.document.formName.item("itemName") 问题 说明:IE下,可以使用document.formName.item("itemName")或document.formName.elements["elementName"];Firefox下,只能使用document.formName.elements["elementName"]. 解决方法:统一使用document.formName.elem

  • js中键盘事件实例简析

    本文实例分析了js中键盘事件.分享给大家供大家参考.具体分析如下: 该实例效果: 按键盘上的任意一个键,弹出相应的ASCII码,兼容ie,chrome和firefox. 但还是有不少问题: (1)ie和chrome中,一些键没有效果,如上.下.左.右等: (2)而firefox中的向右键,与单引号键,都为39. 具体代码如下: 复制代码 代码如下: <html> <head> <script type="text/javascript">  wind

  • Vue数据绑定简析小结

    作为MVVM框架的一种,Vue最为人津津乐道的当是数据与视图的绑定,将直接操作DOM节点变为修改 data 数据,利用 Virtual Dom 来 Diff 对比新旧视图,从而实现更新.不仅如此,还可以通过 Vue.prototype.$watch 来监听 data 的变化并执行回调函数,实现自定义的逻辑.虽然日常的编码运用已经驾轻就熟,但未曾去深究技术背后的实现原理.作为一个好学的程序员,知其然更要知其所以然,本文将从源码的角度来对Vue响应式数据中的观察者模式进行简析. 初始化 Vue 实例

  • Mysql中FIND_IN_SET()和IN区别简析

    前段时间项目中使用到Mysql的FIND_IN_SET函数,感觉挺好用的.过一段时间,老大找到我说,这个需要改为IN,哈哈,只能改了,原因会在下面分析到! 弄个测试表来说说两者的区别,测试数据直接在问答区copy一份,能说明问题就行,哈哈,如果侵犯您的版权还请见谅,互联网吗,就需要分享! 测试代码: CREATE TABLE `test` ( `id` int(8) NOT NULL auto_increment, `name` varchar(255) NOT NULL, `list` var

  • Python函数式编程之面向过程面向对象及函数式简析

    目录 Python 函数式编程 同一案例的不同写法,展示函数式编程 面向过程的写法 面向对象的写法 接下来进入正题,函数式编程的落地实现 Python 函数式编程的特点 纯函数 Python 函数式编程 Python 不是纯粹的函数式语言,但你可以使用 Python 进行函数式编程 典型的听君一席话,如听一席话,说白了就是 Python 具备函数式编程的特性, so,可以借用函数式语言的设计模式和编程技术,把代码写成函数式编程的样子 一般此时我会吹嘘一下,函数式代码比较简洁和优雅~ 好了,已经吹

  • 简析React Native startReactApplication 方法

    在 React Native 启动流程简析这篇文章里,我们梳理了 RN 的启动流程,最后的 startReactApplication 由于相对复杂且涉及到最终执行前端 js 的流程,我们单独将其提取出来,独立成文加以分析. 首先来看 startReactApplication 的调用之处: mReactRootView.startReactApplication( getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOp

  • Oracle的数据字典技术简析

    正在看的ORACLE教程是:Oracle的数据字典技术简析.数据字典是Oracle存放有关数据库信息的地方,其用途是用来描述数据的.比如一个表的创建者信息,创建时间信息,所属表空间信息,用户访问权限信息等.当用户在对数据库中的数据进行操作时遇到困难就可以访问数据字典来查看详细的信息. Oracle中的数据字典有静态和动态之分.静态数据字典主要是在用户访问数据字典时不会发生改变的,但动态数据字典是依赖数据库运行的性能的,反映数据库运行的一些内在信息,所以在访问这类数据字典时往往不是一成不变的.以下

随机推荐