【注意】此文是转载,非原创!!!转载博客:https://www.cnblogs.com/xinxue/p/5471708.html。原创者是博客园的名叫的胡马依北风的博主,十分感谢! ————————————————————————

1  公有继承

公有继承包含两部分:一是 “接口” (interface),二是 “实现” (implementation)

如下 Shape 类中,三个成员函数,代表三种继承方式:

class Shape {
public:virtual void Draw() const = 0;    // 1) 纯虚函数virtual void Error(const std::string& msg);  // 2) 普通虚函数int ObjectID() const;  // 3) 非虚函数
};class Rectangle: public Shape { ... };
class Ellipse: public Shape { ... };

1.1  纯虚函数 (pure virtual)

纯虚函数,继承的是基类成员函数的 接口,且要在派生类中重写该函数的 实现

Shape *ps1 = new Rectangle;
ps1->Draw(); // calls Rectangle::DrawShape *ps2 = new Ellipse; ps2->Draw(); // calls Ellipse::Draw

  若想调用基类的 Draw(),须加上 类作用域操作符 ::

ps1->Shape::Draw(); // calls Shape::draw

1.2  普通虚函数

普通虚函数,对应在基类中定义一个缺省的实现 (default implementation),表示继承的是基类成员函数的接口和缺省的实现,由派生类自行选择是否重写该函数。

  实际上,允许普通虚函数同时继承接口和缺省实现是危险的 如下, ModelA 和 ModelB 是 Airplane 的两种飞机类型,且二者的飞行方式完全相同

class Airplane {
public:virtual void Fly(const Airport& destination);
};
class ModelA: public Airplane { ... };
class ModelB: public Airplane { ... };

这是典型的面向对象设计,两个类共享一个特性 – Fly,则 Fly 可在基类中实现,并由两个派生类继承之

  现增加一个新的飞机型号 ModelC,其飞行方式与 ModelA,ModelB 并不相同,假如不小心忘了在 ModelC 中重写新的 Fly 函数

class ModelC: public Airplane {... // no fly function is declared
};

则调用 ModelC 中的 fly 函数,就是调用 Airplane::Fly,但是 ModelC 的飞行方式和缺省的并不相同

Airplane *pa = new ModelC;
pa->Fly(Qingdao); // calls Airplane::fly!

这就是前面所说的,普通虚函数同时继承接口和缺省实现是危险的最好是基类中实现缺省行为 (behavior),但只有在派生类要求时才提供该缺省行为

1.2.1  方法一 

一种方法是 纯虚函数 + 缺省实现,因为是纯虚函数,所以只有接口被继承,其缺省的实现不会被继承。派生类要想使用该缺省的实现,必须显式的调用

class Airplane {
public:virtual void Fly(const Airport& destination) = 0;
};void Airplane::Fly(const Airport& destination)
{ // a pure virtual function default code for flying an airplane to the given destination
}class ModelA: public Airplane {
public:virtual void Fly(const Airport& destination) { Airplane::Fly(destination); }
};

这样在派生类 ModelC 中,即使一不小心忘记重写 Fly 函数,也不会调用 Airplane 的缺省实现

class ModelC: public Airplane {
public:virtual void Fly(const Airport& destination);
};void ModelC::Fly(const Airport& destination)
{// code for flying a ModelC airplane to the given destination
}

1.2.2  方法二

可以看到,上面问题的关键就在于,一不小心在派生类 ModelC 中忘记重写 fly 函数,C++11 中使用关键字 override,可以避免这样的“一不小心”

1.3  非虚函数

非虚成员函数没有 virtual 关键字,表示派生类不但继承了接口,而且继承了一个强制实现 (mandatory implementation)

  既然继承了一个强制的实现,则在派生类中,无须重新定义 (redefine) 继承自基类的成员函数,如下:

使用指针调用 ObjectID 函数,则都是调用的 Shape::ObjectID()

Rectangel rc; // rc is an object of type RectangleShape *pB = &rc; // get pointer to rc
pB->ObjectID(); // call ObjectID() through pointerRectangle *pD = &rc; // get pointer to rc
pD->ObjectID(); // call ObjectID() through pointer

  如果在派生类中重新定义了继承自基类的成员函数 ObjectID 呢?

class Rectangel : public Shape {
public:int ObjectID() const; // hides Shape::ObjectID
};pB->ObjectID(); // calls Shape::ObjectID()
pD->ObjectID(); // calls Rectagle::ObjectID()

此时,派生类中重新定义的成员函数会 “隐藏” (hide) 继承自基类的成员函数

这是因为非虚函数是 “静态绑定” 的,pB 被声明的是 Shape* 类型的指针,则通过 pB 调用的非虚函数都是基类中的,既使 pB 指向的是派生类

  与“静态绑定”相对的是虚函数的“动态绑定”,即无论 pB 被声明为 Shape* 还是 Rectangle* 类型,其调用的虚函数取决于 pB 实际指向的对象类型

2  重写 (override)

在 1.2.2 中提到 override 关键字,可以避免派生类中忘记重写虚函数的错误

下面以重写虚函数时,容易犯的四个错误为例,详细阐述之

class Base {
public:virtual void mf1() const;virtual void mf2(int x);virtual void mf3() &;void mf4() const;    // is not declared virtual in Base
};class Derived: public Base {
public:virtual void mf1();        // declared const in Base, but not in Derived.virtual void mf2(unsigned int x);    // takes an int in Base, but an unsigned int in Derivedvirtual void mf3() &&;    // is lvalue-qualified in Base, but rvalue-qualified in Derived.void mf4() const;
};

在派生类中,重写 (override) 继承自基类成员函数的实现 (implementation) 时,要满足如下条件:

一虚:基类中,成员函数声明为虚拟的 (virtual)

二容:基类和派生类中,成员函数的返回类型和异常规格 (exception specification) 必须兼容

四同:基类和派生类中,成员函数名、形参类型、常量属性 (constness) 和 引用限定符 (reference qualifier) 必须完全相同

如此多的限制条件,导致了虚函数重写如上述代码,极容易因为一个不小心而出错

C++11 中的 override 关键字,可以显式的在派生类中声明,哪些成员函数需要被重写,如果没被重写,则编译器会报错。

class Derived: public Base {
public:virtual void mf1() override;virtual void mf2(unsigned int x) override;virtual void mf3() && override;virtual void mf4() const override;
};

这样,即使不小心漏写了虚函数重写的某个苛刻条件,也可以通过编译器的报错,快速改正错误

class Derived: public Base {
public:virtual void mf1() const override;  // adding "virtual" is OK, but not necessaryvirtual void mf2(int x) override;void mf3() & override;void mf4() const override;
}; 

小结:

1)  公有继承

  纯虚函数      => 继承的是:接口 (interface)

  普通虚函数   => 继承的是:接口 + 缺省实现 (default implementation)

  非虚成员函数 =>继承的是:接口 + 强制实现 (mandatory implementation)

2)  不要重新定义一个继承自基类的非虚函数 (never redefine an inherited non-virtual function)

3)  在声明需要重写的函数后,加关键字 override

参考资料:

<Effective C++_3rd> item 34, item 36

<Effective Modern C++> item 12

C++新特性:override相关推荐

  1. java 过期注解_JDK新特性-----注解@ (@Override、@Deprecated、@SuppressWarings)

    注解-@Override 准确覆写 为什么会有@Override? 如果子类Student需要覆写父类Object的toString方法: class Student { public String ...

  2. jdk8新特性 lambda表达式详解

    本文主要讲到的内容有: 一- 前言 二- 背景 三- lambda表达式的语法 四- Lambda程序例子 4-1 Runnable Lambda 4-2 Comparator Lambda 4-3 ...

  3. Java基础20:Java8新特性终极指南

    更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...

  4. JDK1.5 新特性

    Java 5.0发布了,许多人都将开始使用这个JDK版本的一些新增特性.从增强的for循环到诸如泛型(generic)之类更复杂的特性,都将很快出现在您所编写的代码中.我们刚刚完成了一个基于Java ...

  5. C#6.0,C#7.0新特性

    C#6.0,C#7.0新特性 C#6.0新特性 Auto-Property enhancements(自动属性增强) Read-only auto-properties (真正的只读属性) Auto- ...

  6. JAVA8 十大新特性

    一.接口的默认方法 Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法,示例如下: 复制代码代码如下: interface Formula ...

  7. Entity Framework 4.3 中的新特性

    原文地址:http://www.cnblogs.com/supercpp/archive/2012/02/20/2354751.html EF4.3于2月9号正式发布了,微软的EF小组最近一年开始发力 ...

  8. 返璞归真 asp.net mvc (10) - asp.net mvc 4.0 新特性之 Web API

    返璞归真 asp.net mvc (10) - asp.net mvc 4.0 新特性之 Web API 原文:返璞归真 asp.net mvc (10) - asp.net mvc 4.0 新特性之 ...

  9. Java8新特性学习记录

    前言: Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级.在Java Code Geeks上已经有很多介绍Java 8新特性的文章, 例如Playing with Java ...

  10. 魅族android p,2.Android P新特性实践-Slices

    Android P新特性实践-Slices 概述 在2018年谷歌I/O开发者大会上,谷歌发布了万众期待的Android最新版本-Android P.Slice作为Android P版本更新的一大功能 ...

最新文章

  1. [HNOI2019]JOJO
  2. java rect平移_如何在Java Swing中使用鼠标平移图像
  3. 错误代码#1045 Access denied for user 'root'@'localhost' (using password:YES)
  4. Java对象初始化执行顺序
  5. 后端开发(1)---大话后端开发的技巧大集合
  6. python同时满足两个条件_python算法-快速寻找满足条件的两个数
  7. ACM题目————中位数
  8. requests 获取百度推广信息
  9. 【算法】排序_快速排序
  10. 电话机器人源码厂家哪家比较有研发实力?
  11. pytorch修改图片尺寸大小
  12. NOI2022退役记
  13. linux实用教程复习题,linux上机复习题(部分答案)
  14. SEO优化基础知识大全 SEO新手入门必备知识
  15. 能够边下边看bt资源的工具:Tribler Mac中文免费版
  16. 酷狗音乐皮肤怎么从计算机里换,酷狗音乐怎么换皮肤 酷狗音乐自定义皮肤的方法...
  17. android键盘坏了怎么办,手机虚拟键盘失灵怎么办
  18. han_attention(双向GRU+attention)(imdb数据集---文档分类)
  19. 英国内政部(Home Office)间谍机构(spy powers)假装它是Ofcom咨询中的一名私人公民1514378369635...
  20. php电脑端打开微信页面大小,电脑微信小程序设置全屏的方法是什么?

热门文章

  1. 5G DL PRS(Positioning Reference Signal) -- 下行定位参考信号
  2. rep论文阅读2:ResRep_Lossless CNN Pruning via Decoupling Remembering and Forgetting
  3. 如何用企业网站的访问统计数据助力线上推广 上海添力
  4. 聊一聊前端性能优化 CRP
  5. 工程师的基本功是什么?听听美团技术大咖怎么说
  6. matlab gain 可以是负数吗,GAMS不报错,但是得负数,怎么办?
  7. PuTTY 下载安装教程
  8. 杭州软件测试人员工资水平,杭州软件测试工资待遇如何?
  9. Flutter实践——AndroidStudio环境初体验
  10. 为Termux安装图形化界面