你是一名。你知道C是适合该工作的语言,尽管有时维护周期可以重复很长时间。有时你会感到that琐,就像在编码自动机一样,反复在结构上创建基本迭代,这些结构与上周或上个月的结构非常相似。
你已经听说过C ++作为一种功能强大的语言的销售趋势,但是你也听说过有关C ++巨大足迹的恐怖故事,这使C ++成为嵌入式应用的不二之选。此外,它是如此复杂:必须确实很难使用。
这有一个熟悉的戒指吗?嵌入式开发市场嵌入式开发市场涵盖了广泛的应用领域,包括汽车,医疗,国防和电信。尽管C作为嵌入式开发的强大语言嵌入式开发的强大语言无疑享有良好的声誉,但C ++却没有那么广泛的吸引力。在当今功能强大且复杂的C ++环境中,这通常是不正确的信念。
为什么C ++可以替代C?作为一种语言,它当然是从C语言的根源发展而来的。仅使用aC ++编译器重新编译C项目将产生更严格的代码类型检查(可能需要先克服一些声明上的不兼容性)。
一旦掌握了其核心功能,C ++就可以提供更大的数据抽象,这对于更大,更复杂的软件系统来说是一个重要的目标。面向对象(OO)使这一抽象更进一步,你可以用类功能替换全局“工作者”功能。
模板可能是抵制C ++使用的最大恐惧因素,并且经常被引用为大型代码膨胀经验的原因或推测。但是,实际上,精心设计的模板代码提供了一种优雅的方式来一致地处理各种数据类型。C ++的StandardLibrary是此类通用编程风格的广告。
异常处理是另一个遭受FUD(恐惧,不确定性和怀疑)困扰的领域。许多C软件系统中的异常通常是手动的编程任务。可以用更优雅的基于C ++异常的解决方案替换此异常处理机制,尽管需要付费。
与往常一样,只有在对开发团队进行需求更改时,这种优雅的解决方案才能得到应有的认可。精心设计的基于对象的设计在这种情况下可以适应的速度部分归因于更好的实现抽象和隐藏。
编译器性能
坦率地说,比较C和C ++编译器实现可能令人讨厌。从这种苹果与橘子的比较中很难获得科学的结果。
有趣的新闻组讨论报告了C ++实现的空间和性能效率损失在8%到30%之间。当然,对于使用相同Clibrary的完全相同的代码,我们希望使用相同的二进制代码。
在其产生的过程中,毫无疑问在速度方面,特别是在可执行和运行时空间需求方面,C ++性能的经验很差。在C ++的采用初期,这些担忧导致了有趣的行业形成。
十年前,主要是日本嵌入式系统提供商的合作提出了一种严格受限的C ++语言语言环境,以满足嵌入式需求。该概念禁止使用C ++语言,包括异常处理,运行时类型信息,命名空间,模板,多重继承和虚拟函数。
其理由有两个主要方面。首先是消除导致可怕的“代码膨胀”的语言构造。第二个目的是从学习的角度简化C ++语言,也许是因为嵌入式工程师的OO经验水平较低。根本有缺陷
但是,在充分尊重作者的前提下,EmbeddedC ++的方法似乎存在根本缺陷。C ++语言委员会的勤奋工作的成员一定听说过放弃其精心设计的标准C ++语言的全部内容的提议,对此感到灰白。
他们对该建议的集体回应是开始审查该语言及其最受欢迎的实现(编译器和机器环境)中的实际性能问题。
结果是C ++标准委员会发布的一份全面报告,内容涉及完整C ++语言每个主要功能的性能预期。报告中有一个小节介绍了每个功能的性能和空间效率(静态和运行时)。本文总结了最有趣的功能的发现。
a)命名空间:与使用命名空间没有空间或时间开销。它们仅在编译时影响名称查找规则。命名空间的主要优点是提供了一种在大型项目中对名称进行分区的机制,以避免名称冲突。顺便说一句,using指令通过将所有未限定的标识符移动到当前名称空间中,避免了在使用显式名称空间限定时进行额外的键入工作。
b)TypeConversions: C ++带有C样式转换符号,但是通过四个适用于不同转换情况的newoperator支持更安全和显式的转换。对于这些新样式转换操作符中的三个(const_cast,static_cast和reinterpret_cast),不涉及性能。
实际上,编译器在生成目标代码时通常会将转换符号转换为这些新的类型转换运算符之一。如果需要的转换需要运行时类型信息(RTTI)机制(例如,在类层次结构中进行交叉转换),则只有dynamic_cast可能会涉及额外的开销,我们将在后面介绍。
c)ClassRepresentation: 与C的structand全局函数等效项相比,基本的类功能并不感到任何空间或速度开销,这有些令人惊讶。这是因为非虚拟函数和静态数据成员与类定义一起存储,而不是存储在类的对象中。
成员函数调用还有一个额外的隐式指针参数,必须指向类对象(* this)。另一方面,独立的函数调用通常需要通过等效的显式指针参数将操作数据明确地传递给它们。
d)VirtualFunctions: 虚拟函数会产生明确定义的成本,该成本基于基础操作:索引到函数指针数组中。这是一种在Ccode中很常见的实现技术,但在虚拟函数范式中表达得更为优雅。
在某些情况下,使用虚函数可能会导致“代码膨胀”。如果包含虚拟函数的类模板针对各种类型进行了专门化,则每个专门化都将保留重复的成员函数及其相关的支持结构,包括虚拟表。
通常,这将导致过多的目标代码,因为当前的链接器/优化器技术不够成熟,无法识别这种情况。为避免此问题,你可以将通用代码(不依赖于实例化类型)移出类模板并移入非模板帮助器函数,或者将功能性从模板类移至非模板基类。
e)FunctionInlining: 就提高性能效率而言,应避免使用函数调用。C ++的内联功能为编译器提供了可以内联到其调用位置的函数的提示。编译器没有义务接受此提示。先进的优化技术可以自动识别和删除较小且不太复杂的函数调用,而无需代码明确提供内联提示。但是,迄今为止的经验表明,隐式内联不会产生一致的好处,因此应该使用显式内联关键字。
f)虚拟基类(VBC):在非虚拟继承中,成员函数调用执行简单的常量调整。与VBC的本质区别在于成员函数需要在运行时执行查找,以发现应激活继承树中的哪个类函数。与非虚拟案例相比,这涉及大约15%的额外开销。
但是,通过另一个功能来模拟虚拟呼叫的功能也会带来成本。实现在组成类周围传递的接口类的另一种技术本身确实需要在访问中使用间接方式,从而导致随之而来的成本和开销。
g)运行时类型信息:
RTTI用于询问对象的类型,它也是dynamic_cast功能基础结构的一部分。为了说明其性能,请考虑使用dynamic_cast的应用程序:查找对象的vtable,找到该对象的派生对象最多的对象,使用该对象的type_info对this指针执行所需的调整。
h)异常处理: C ++的异常处理功能在运行时需要类型信息,并且与RTTI结构部分重叠并扩展了RTTI结构。但是,任何手动编码备选方案都必须考虑编码风格,错误处理例程的完整覆盖,线程安全性,运行时系统开销以及处理错误的开销。考虑相关的开销,运行时开销和代码维护开销可以进行异常处理合理的选择。
i)模板:就空间成本而言,模板是C ++最受人谴责的功能之一。当类和函数模板为具有不同参数的每个实例生成一组新的代码和数据时,就会出现代码膨胀。
对相同专业化的多个实例化与许多不同专业化进行的测试表明,性能结果差异很大,这表明编译器有一些方法可以满足该领域的优化目标。实施优化
通过启用某些功能(尤其是部分专业化功能),编译器可以允许图书馆供应商实施优化以克服此问题。开发人员可以通过通用类模板路由所有实例化请求,例如通过对基于void *的单个专业化使用通用类模板,来部署避免多个专业化的技术。
尽管此分析可能会使你更熟悉C ++作为一种开发语言,但它本身并不能帮助你创建良好,可靠且尤其是高性能的C ++代码。
对于C ++语言中的优化问题,有很多建议,包括初始化优先于赋值,避免不必要的转换,避免临时对象的创建(参数传递,函数返回和表达式)以及内联的明智使用。除了基于语言的优化之外,还建议正确使用C ++的出色库。
编码标准实施(CSE)工具的使用提供了一个有用的安全网,该网络可让你自由创建C ++代码,但在编写可疑代码时会及时发出警告。例如,PRQA的QAC ++ advancedanalyzer工具标记了不可移植,难以维护,过于复杂,不符合ISO或以任何可能引起问题的方式编写的代码。
消息浏览器在整个项目的源代码上显示警告消息。这些在所有源文件中进行了分类和分组。浏览器链接到有用的附加信息,以及有关分析仪识别出的编码违规的建议。
可以按文件或项目分析源代码,以识别潜在的危险语言用法,以防止错误爬入你的系统。该分析仪能够找到代码中的晦涩错误,从而大大减少了调试时间。
但是,让我们回到C开发人员的心态。通常,人们认为使用C ++开发与传统的C开发相比需要采取截然不同的方法。如果我们研究两种典型的实现技术,我们可能得出不同的结论。
C中的多态性
一条记录需要存储不同类型的数据,其定义可能如下所示:
然后,对该记录进行操作的所有代码都必须检查其代表的数据类型:
但这看起来很像多态概念。成员类型对应于类vtable指针,而switch语句中的代码段对应于一组虚函数。
考虑到使用此记录时此类转换语句的泛滥,多态替代方案更加优雅且易于维护。甚至没有最大填充类型的数据填充的好处,就像这里必须发生的那样。
C中的代码膨胀在
典型的C项目中,你经常会看到迭代代码,例如:
C ++库通过将丰富迭代功能放入其容器类集中来针对这种类型的重复代码。与通常使用手工编码的替代方法相比,它提供了更易于维护,紧凑且最可能更快的实现。嵌入式学习路线
因此,性能能力的这种证明也许并不会导致你立即将嵌入式开发转换为C ++语言-毕竟,选择轻松进行并非易事。但是,随着项目规模的扩大,它们越来越需要精心设计和管理。
在这种情况下,C ++完全可以替代C。基于PRQA自身的经验,C ++在可维护性以及对变更和扩展要求的响应方面提供了很高的回报。
在嵌入式开发的更严格要求下,这些研究结果表明,只要稍加注意和注意,并借助CSE工具的附加安全网络,C ++就可以提供很多功能。