C++ override及虚函数的讲解

xiaoxiao2021-02-28  22

胡马依北风 随笔 - 46  文章 - 0  评论 - 11  博客园   首页   新随笔   联系   管理   订阅 

C++11 之 override

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::Draw Shape *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 Rectangle Shape *pB = &rc; // get pointer to rc pB->ObjectID(); // call ObjectID() through pointer Rectangle *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 Derived virtual 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 necessary virtual 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++11 好文要顶  关注我  收藏该文    胡马依北风 关注 - 4 粉丝 - 29 +加关注 2 0 « 上一篇: C++ 之 重载赋值操作符 » 下一篇: C++ 之 策略模式 posted on  2016-05-11 20:58  胡马依北风 阅读( 7928) 评论( 0)  编辑  收藏 刷新评论 刷新页面 返回顶部 注册用户登录后才能发表评论,请  登录 或  注册, 访问网站首页。 【推荐】50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库 【推荐】Vue.js 2.x 快速入门,大量高效实战示例 【活动】腾讯云 学生专属优惠套餐 多规格选择 最新IT新闻: ·  ofo越过山丘 ·  360重组议案过关股东大会500亿元资产注入在即 ·  美国邮政推出VR应用 但并不是提供有关邮寄信件的信息 ·  柳传志撰文:我为湖畔大学正名 ·  趣店数据疑似外泄,十万可买百万学生信息 »  更多新闻... 最新知识库文章: ·  大道至简,职场上做人做事做管理 ·  关于编程,你的练习是不是有效的? ·  改善程序员生活质量的 3+10 习惯 ·  NASA的10条代码编写原则 ·  为什么你参加了那么多培训,却依然表现平平? »  更多知识库文章...

公告

昵称: 胡马依北风 园龄: 1年9个月 粉丝: 29 关注: 4 +加关注 <2017年11月 > 日 一 二 三 四 五 六2930311234567891011121314151617181920 21222324252627282930123456789

搜索

   

常用链接

我的随笔我的评论我的参与最新评论我的标签

我的标签

OpenCV(10)C++(8)C++11(5)Qt(5)Verilog(2)模式识别(2)Linux(2)Machine Learning(1)Deutsch(1)设计模式(1)更多

随笔分类

机器视觉(1) 逻辑设计(7) 软件编程(24) 图像处理(10) 易北往事(3)

随笔档案

2017年9月 (1) 2017年8月 (3) 2017年7月 (2) 2017年6月 (2) 2017年5月 (1) 2017年2月 (3) 2017年1月 (1) 2016年11月 (2) 2016年10月 (1) 2016年9月 (2) 2016年8月 (2) 2016年7月 (4) 2016年6月 (4) 2016年5月 (4) 2016年4月 (4) 2016年3月 (3) 2016年2月 (3) 2016年1月 (4)

最新评论

1. Re:OpenCV 之 编译和配置 @xiake007生成了,已经更新了新的记录... --胡马依北风 2. Re:机器视觉 之 齐次坐标 @胡马依北风lichongbin2004@126.com,谢谢!... --lichongbin 3. Re:机器视觉 之 齐次坐标 @lichongbin有电子版,需要的话可留个邮箱,发给你 ...... --胡马依北风 4. Re:机器视觉 之 齐次坐标 你给出的这三本参考书哪里有的卖? --lichongbin 5. Re:OpenCV 之 编译和配置 写得真详细,后来问题怎么样了?生成成功了吗? --xiake007

阅读排行榜

1. C++11 之 override(7926)2. Qt 之 入门例程(4085)3. C++11 之 delete 和 default(3942)4. OpenCV 之 边缘检测(2646)5. OpenCV 之 编译和配置(1941)6. OpenCV 之 霍夫变换(1621)7. OpenCV 之 图像平滑(1575)8. Qt 之 饼图(1561)9. Qt 之 QtConcurrent(1521)10. OpenCV 之 神经网络 (一)(1352)

评论排行榜

1. OpenCV 之 编译和配置(3)2. 机器视觉 之 刚体变换(3)3. C++11 之 delete 和 default(2)4. OpenCV 之 图像平滑(2)5. C++ 之 策略模式(1)

推荐排行榜

1. C++11 之 scoped enum(4)2. C++ 之 策略模式(4)3. OpenCV 之 图像分割 (一)(4)4. Qt 地址薄 (一) 界面设计(2)5. C++11 之 override(2)6. OpenCV 之 图像平滑(2)7. C++11 之 nullptr(2)8. OpenCV 之 边缘检测(2)9. C++笔记 之 基础回顾(一)(2)10. OpenCV 之 编译和配置(1) Copyright ©2017 胡马依北风
转载请注明原文地址: https://www.6miu.com/read-2050094.html

最新回复(0)