1.4.6 与硬件紧密合作
在C++编程中,嵌入式编程是一个非常重要的领域。虽然一些方方圆圆的智能设备外表光鲜亮丽,但是植根于其中的技术基础也常常会是C++。在C++11中,常量表达式以及原子操作都是可以用于支持嵌入式编程的重要特性。这些特性对于提高性能、降低存储空间都大有好处,比如ROM。
C++98/03中也具备const类型,不过它对只读内存(ROM)支持得不够好。这是因为在C++中const类型只在初始化后才意味着它的值应该是常量表达式,从而在运行时不能被改变。不过由于初始化依旧是动态的,这对ROM设备来说并不适用。这就要求在动态初始化前就将常量计算出来。为此标准增加了constexpr,它让函数和变量可以被编译时的常量取代,而从效果上说,函数和变量在固定内存设备中要求的空间变得更少,因而对于手持、桌面等用于各种移动控制的小型嵌入式设备(甚至心率调整器)的ROM而言,C++11也支持得更好。
在C++11,我们甚至拥有了直接操作硬件的方法。这里指的是C++11中引入的原子类型。C++11通过引入内存模型,为开发者和系统建立了一个高效的同步机制。作为开发者,通常需要保证线程程序能够正确同步,在程序中不会产生竞争。而相对地,系统(可能是编译器、内存系统,或是缓存一致性机制)则会保证程序员编写的程序(使用原子类型)不会引入数据竞争。而且为了同步,系统会自行禁止某些优化,又保证其他的一些优化有效。除非编写非常底层的并行程序,否则系统的优化对程序员来讲,基本上是透明的。这可能是C++11中最大、最华丽的进步。而就算程序员不乐意使用原子类型,而要使用线程,那么使用标准的互斥变量mutex来进行临界区的加锁和开锁也就够了。而如果读者还想要疯狂地挖掘并行的速度,或试图完全操控底层,或想找点麻烦,那么无锁(lock-free)的原子类型也可以满足你的各种“野心”。内存模型的机制会保证你不会犯错。只有在使用与系统内存单位不同的位域的时候,内存模型才无法成功地保证同步。比如说下面这个位域的例子,这样的位域常常会引发竞争(跨了一个内存单元),因为这破坏了内存模型的假定,编译器不能保证这是没有竞争的。
struct{int a:9;int b:7;}
不过如果使用下面的字符位域则不会引发竞争,因为字符位域可以被视为是独立内存位置。而在C++98/03中,多线程程序中该写法却通常会引发竞争。这是因为编译器可能将a和b连续存放,那么对b进行赋值(互斥地)的时候就有可能在a没有被上锁的情况下一起写掉了。原因是在单线程情况下常被视为普通的安全的优化,却没有考虑到多线程情况下的复杂性。C++11则在这方面做出了较好的修正。
struct{char a;char b;}
与硬件紧密合作的能力使得C++可以在任何系统编程中继续保持领先的位置,比如说构建设备驱动或操作系统内核,同时在一些像金融、游戏这样需要高性能后台守护进程的应用中,C++的参与也会大大提升其性能。
我们会在第6章看到相关特性的描述。