0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

MISRA C++针对使用多态性的规避方法与建议

电子设计 来源:单片机与嵌入式系统应用 作者:王玺 , 邵贝贝 2020-09-07 18:41 次阅读

多态性是C++的一个重要特征。从广义上说,多态性是指一段程序能够处理多种类型对象的能力;具体地讲,多态性就是对不同对象发出同样的指令时,不同对象会有不同的行为。

如果程序员充分利用C++的多态性,设计程序的运行方式会更加灵活多样,但是会带来一些暗藏的细节问题。这些细节的漏洞也许会通过编译,但是在某些情况下,不可预测的结果或者背离编程者初衷的结果都会导致程序变得混乱不堪,甚至产生较大的风险。为了规避这些风险,MISRA C++推荐了一些编程规则。这些规则能够帮助程序员更加完备或者完美地实现多态性,充分体现C++相比于传统C语言的一些优势。

本文主要介绍两类在实现形式的多态性中需要注意的一些问题:一是运算符的重载,这是编译时的多态性,即程序在编译时就能根据重载的情况确定需要调用的函数;二是虚函数的使用,这是运行时的多态性,即在程序执行前,无法根据函数名和参数来确定调用哪个函数,必须在程序执行过程中,根据执行的具体情况来动态确定。

1 运算符的重载

运算符重载就是定义某个运算符对于某个类的具体含义。通过运算符的重载,程序员可以针对一些特定的类型使用重载的运算符含义。

规则5-2-11(强制):逗号(,),与(&&)以及或(||)运算符不允许被重载。

如果getValue和setValue的返回类型使用重载运算符&&,则这两个函数都需要计算。

C++的内部规定是,&&和||都是在已知结果的情况下不再计算后面的值,比如0&&(a--)&& (b++)。然而重载&&运算符和||运算符导致了程序运行时要计算所有的表达式。这对于一些使用&&做判断的运算来说,会导致一些错误。比如getchar()&&putchar(),在读取文件时,如果读到文件尾部,即得到getchar()为0时,就不需要再执行putchar()了,这样才能正确地读取并输出文件。如果重载&&运算符,那么先需要计算getchar()和 putchar()的结果,再执行&&运算符的重载定义,这样可能会导致一些不可知的错误。这样的重载,会导致编译器在处理&&和||运算符时产生混乱,所以是比较危险的。

对于逗号表达式来说,默认情况下,编译器按照逗号表达式规定的顺序计算各个表达式。但是如果重载操作逗号表达式,因为需要先检查逗号两边的表达式类型,来判断是否使用重载定义的类型,所以会导致计算顺序的混乱。这样比较危险,会产生一些不可知的错误。虽然在C++并没有限制这3个运算符的重载问题,但是从这个例程和MISRA C++的规则来看,有些时候会产生一些不可预知的错误,所以MISRA C++不允许重载上面3个运算符。

规则5-3-3(强制):单目运算符&不允许被重载。

f1.cc和f2.cc的区别就在于f1.cc只声明了A类,而f2.cc包含了A.h。f1.cc仅声明A类,不会使用A类定义的重载运算,所以 f1.cc的8L运算符使用C++内部的取地址定义。f2.cc包含了头文件A.h,因为A.h包含了A类的完整定义,所以f2.cc的&运算符就会使用用户定义的重载操作。在同样一个工程中,仅仅是对A类的声明不同,就导致了在f2.cc中,&a使用用户定义的&运算符含义,而在f1.cc中,&a使用C++内部定义的&运算符含义。

这样差别会导致程序员在重载&运算符后,无法得知&运算符有没有使用重载的定义。这样做是比较危险的,可能会产生与程序员意愿不同的结果。虽然在C++中并没有限制对单目运算符的重载操作,但是从上面的例程可以看出,MISRA C++不允许重载&运算符是很有必要的。

2 虚函数的使用

虚函数是C++中一类特殊的函数。在基类中定义一个虚函数,就说明该函数在派生类中可能有不同的实现方式。当派生类的实例调用这个虚函数时,首先会在派生类中去查看该函数有没有被定义。如果派生类定义了这个函数,则执行派生类的函数;否则,在派生路径上寻找最近的该函数的定义,并调用该函数。

如果从基类派生出多个派生类,那么每个派生类都可以重新定义这个虚函数。如果通过基类的指针指向派生类的对象,并访问该虚函数,会对应地调用每个派生类的函数定义。这样通过基类类型的指针,就可以使属于不同派生类的对象产生不同的行为,从而实现了运行过程的多态。

关于虚函数,MISRA C++有以下几条规则:

规则10-3-1(强制):在每一个继承路径上,虚函数只能有一个定义。防止按优先度调用。

例外:析构函数可以定义为虚函数,在每一个派生类上都可以有定义。

如果一个函数在同一个类中被声明为纯虚函数,但是还有定义,这样的定义就会被忽略。

在例程的后半段是关于按优先度调用的解释,表1显示的是例程中每个函数的调用和定义关系。b2.f1()是按照正常的继承关系来调用foo()函数,并且调用的是V类中foo()的定义。d.f2()和d.f1()都是按照优先度调用的。它们虽然最后都是调用了foo()函数,但是经过的继承路径却不相同,而且它们最后只能调用到B1类中foo()的定义。为了防止这种情况发生,所以Misra C++规定,虚函数在一个继承路径上,只能有一个函数定义。

例程的前半部分描述了多个类的继承关系,每个类都包括对几个函数的定义和声明。这里简单介绍一下f1()函数,读者可以通过表2的内容来理解其他函数。 f1在A类中是虚函数,而且有定义,在C类中有定义,所以当D类继承C类时,D类中就不能再有定义(“√”表示可以定义,“*”表示不推荐再继续定义)。例外是f4,虽然它在A类中有定义,但是因为它是纯虚函数,所以它的定义会被忽略。

这个规则说明,如果在一个继承路径上有两个函数定义,在调用函数时,有可能按照继承的优先度调用函数。这样就会导致函数调用的混乱,可能会调不到程序员希望的函数。这是在实现多态时需要特别注意的地方。关于继承路径上的函数定义,C++并没有明确限制。

从上面的例程可以看出,如果没有这样的限制,就会产生一些混乱,虽然程序能够正常运行,但是不一定能够按照程序员所设计的方式运行。这样的运行方式会出现很多漏洞,所以MISRA C++强制规定在每一个继承路径上,虚函数只能有一个定义。

规则10-3-2(强制):每一个重载的虚函数应该用关键字virtual来声明。

这样做不需要检查基类,就可以确定函数是否为虚函数。MISRA C++推出这样的规则是为了使C++程序更加完善。

规则10-3-3(强制):只有被声明为纯虚函数的虚函数,才能被纯虚函数重载。

foo函数在A类中定义为纯虚函数,在B类中被重载为普通虚函数。而C类使用纯虚函数重载foo函数。这样做是不行的。

B类中foo函数重载A类的foo函数时,是用有定义的虚函数重载纯虚函数,这样做是可以的。

C类中的foo函数重载B类的foo函数时,是用纯虚函数重载一个非纯虚函数,这样是不行的。在C类中,foo被定义为纯虚函数,在C类的对象调用foo 函数时无法调用到B类中的定义。这样的重载导致B类中对foo函数的定义丢失。

所以MISRA C++不允许使用纯虚函数重载非纯虚函数,这样做的目的也是为了使C++程序更加安全。

3 小结

正确并完备地实现C++的多态性,能够充分发挥C++的优势,并且提高程序的可读性和可维护性。如果使用不当,会导致一些想象不到的程序漏洞。MISRA C++针对使用多态性可能产生的一些漏洞,提出了规避的方法与建议。本文列出了其中几条比较关键和实用的规则。关于多态性的其他规则,读者可以查看。 MISRA C++(2008),以避免不正确使用多态性所导致的一些程序漏洞。

责任编辑:gt

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C++
    C++
    +关注

    关注

    21

    文章

    2070

    浏览量

    73038
  • 编译器
    +关注

    关注

    1

    文章

    1588

    浏览量

    48797
收藏 人收藏

    评论

    相关推荐

    IC攻城狮求职宝典 06 比特大陆 笔试题

    就是指不同的实现,即执行不同的函数。这种多态性使得,在非必需的情况下我们可以从一个现存的基类里面继承与共享一些变量与子程序得到扩展类。而扩展类只需要有选择的改变或增加其部分方法与属性。 更多详见附件
    发表于 01-04 15:55

    为什么Harmony生成c而不生成c ++?

    多态性的语法机制。多态性是面向对象编程(OOP)的概念,它允许不同类型(或类)的数据(或其他对象)支持多种形式。为了在C语言中实现这种灵活性,模块必须将指针转换为内部定义的数据类型。但是,将
    发表于 03-25 08:06

    如何完备地实现C++多态性

    如何完备地实现C++多态性?虚函数怎么使用?
    发表于 04-28 06:44

    C++程序设计精简版

    章 指针第 7 章 自定义数据类型 第 3 篇 基于对象的程序设计第 8 章 类和对象第 9 章 关于类和对象的进一步讨论第 10 章 运算符重载 第 4 篇 面向对象的程序设计第 11 章 继承与派生第 12 章 多态性与虚函数第 13 章 输入输出流第 14 章 C++
    发表于 10-09 07:26

    利用P2P的网络特性构造多态性密码

    为了解决没有第三方认证的情况下,P2P 网络通信过程中对等点的授权问题,本文基于P2P 网络中对等点的信任度管理,提出了利用P2P 网络结构的特点来构造一个多态性密码的新方法
    发表于 06-20 09:35 8次下载

    基于Java多态性的应用程序设计

    Java 中的多态体现在类的继承和实现接口等方面。通过对与多态有关的概念进行归纳比较,从继承和接口两方面对多态的正确实现进行分析,结合实例说明多态性在程序设计中的
    发表于 09-09 08:51 24次下载

    什么是方法的重载(多态性)?

    什么是方法的重载(多态性)? 在同一个类中至少有两个方法用同一个名字,但有不同的参数。
    发表于 04-28 14:28 1242次阅读
    什么是<b class='flag-5'>方法</b>的重载(<b class='flag-5'>多态性</b>)?

    C++的动态多态和静态多态

    多态C++ 中面向对象技术的核心机制之一包含静态多态和动态多态它们之间有一定的相似性但是应用范围不同该文论述了这种相似性并重点论述了以模板实现的静态
    发表于 06-29 15:41 41次下载
    <b class='flag-5'>C++</b>的动态<b class='flag-5'>多态</b>和静态<b class='flag-5'>多态</b>

    数据的共享和保护以及多态性_实验4

    c++面向对象课程实验指导书题目_数据的共享和保护以及多态性
    发表于 01-14 16:25 0次下载

    java多态性的实现

    Java中多态性的实现 什么是多态 面向对象的三大特性:封装、继承、多态。从一定角度来看,封装和继承几乎都是为多态而准备的。这是我们最后一个概念,也是最重要的知识点。
    发表于 09-27 10:36 9次下载

    探讨C++多态性技术的局限性及解决的办法

    多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为。
    的头像 发表于 01-08 11:06 3525次阅读

    C++程序设计教程之多态性与虚函数的详细资料说明

    本文档详细介绍的是C++程序设计教程之多态性与虚函数的详细资料说明主要资料包括了:1 多态性的概念,2 一个典型的例子,3 虚函数,4 纯虚函数与抽象类
    发表于 03-14 16:39 5次下载
    <b class='flag-5'>C++</b>程序设计教程之<b class='flag-5'>多态性</b>与虚函数的详细资料说明

    C++中如何用虚函数实现多态

    01 — C++虚函数探索 C++是一门面向对象语言,在C++里运行时多态是由虚函数和纯虚函数实现的,现在我们看下在C++中如何用虚函数实现
    的头像 发表于 09-29 14:18 1536次阅读

    多态性实现原理及其在面向对象编程中的应用

    在面向对象的编程中,多态性是一个非常重要的概念。
    的头像 发表于 06-08 14:19 480次阅读

    您需要了解的有关下一个MISRA®标准的信息:MISRA C++ 2023®简介

    MISRA C++:2023®是广受期待的MISRA C++ ®标准的下一个版本,将于今年晚些时候发布。新版本将整合AUTOSAR C++14指南,并支持
    的头像 发表于 08-25 18:06 1043次阅读
    您需要了解的有关下一个<b class='flag-5'>MISRA</b>®标准的信息:<b class='flag-5'>MISRA</b> <b class='flag-5'>C++</b> 2023®简介