嵌入式C++编程准则
A. 代码从C语言到C++语言的移植
A.1 字符常量
注解
在C中,字符常量是int类型,而在C++中,其类型为char.
示例
I = sizeof(‘a’);
在C中,i保存的实际内容是sizeof(int)的计算结果值,该值要比1大。而在C++中,i存储的实际内容是sizeof(char),该值总是为1.
准则
当将C代码移植为C++代码时,所有对字符常量的sizeof有依赖关系的表达式都要被移除。
A.2 文件范围内的对象声明
注解
在C++中,在文件范围内,声明一个没有指定存储类型标志符的对象时,实际上是定义该对象为外部链接符号(extern)。如果这个定义中没有初始化表达式,那么该对象将被初始化为0.相比较于C来说,C++程序中声明的对象会被明确的定义且只定义过1次。
而在C中,这将被视为一个暂时的定义,这种定义方式在一个translation unit中允许出现多次。
示例
int a; /* (1) */
int a = 10; /* (2) */
在C中,(1)是一种暂时性的定义。由于(2)被看作是确切的定义,(1)被看作是只是声明一个变量。
在C++中,(1)、(2)都被认为是定义式。由于存在(1)、(2)两个重复定义式,因此是一个错误。
准则
在C++中,如果想在文件范围内仅声明一个对象(而不是定义该对象),那么该对象不能拥有初始化表达式,并且需要使用extern来修饰。
声明于文件范围内的每一个对象只能显式的被定义一次。除了一次声明外,其余所有声明都必须具有extern修饰并且没有初始化表达式。
A.3 const 类型修饰符
注解
在C中,在文件范围内,一个使用const修饰而未指明存储类型的对象具有外部链接属性。而在C++中,它具有内部连接属性。
示例
+- file1 --------------------+
| extern const int n; |
+----------------------------+
+- file2 --------------------+
| const int n = 10; |
+----------------------------+
在C中,文件file2中的对象n具有外部连接属性,因此,文件file1中对象n(该对象同样具有外部链接属性)可以引用它。在C++中,文件file2中的对象n具有的属性是内部链接的,因此,file1中的对象n无法引用到它。
准则
使用extern显示修饰const对象,使该对象具有外部链接属性。
A.4 void*的转型
注解C语言标准允许void*转换为T*(T表示任何对象),而在C++中没有这样的标准。在C++中类似的转换需要显示转换来完成。
下面这些C标准库函数都返回void*:
calloc, malloc, realloc, bsearch, memcpy, memmove,
memchr, memset
在C++中,将这些函数的返回值赋值给一个非void*的指针时,需要显示转换。
示例
int* p;
p = malloc(10 * sizeof(int));
在C++中,对指针p的赋值需要显示的转换,如:
p = (int *)malloc(10 * sizeof(int));
准则
在C++中使用new来代替calloc、malloc、realloc(参考A.12);
忽略memcpy, memmove, 以及memset的返回值(通常这些返回值由函数的第一个参数转换而来);对于其他所有返回void*函数(包括标准库函数和用户自定义函数),需要使用显式的转换来将返回值转换为其他指针类型。
A.5 枚举类型
注解
C中的枚举变量是整型。程序中,枚举类型和整型可以互相转换,而不需要显式的转换。C程序允许枚举类型对象进行++和——运算。
C++中的枚举是一种用户自定义类型。C++标准允许枚举类型转换为整型,但从整型转换为枚举类型是非法的。C++程序中还不能将内置的++和—以及符合运算符(如+=)作用于枚举类型变量上。
示例
enum RGB { red, green, blue } rgb;
++rgb;
如果表达式(++rgb)采用内置的++运算符,那么在C++中这是一个错误表达式。该表达式的语意相当于:
rgb = rgb + 1;
上面的表达式在C++中还是错误的。而像下面这样,对枚举值进行显示的转换就是正确的:
rgb = RGB(rgb + 1);
最好的解决方法是为枚举类型RGB实现一个++运算符。
RBG &operator++(RGB &x)
{
return x = RGB(x + 1);
}
准则
将C程序移植为C++程序时,在需要的时候,要为枚举类型实现类型安全的++和——操作符。
A.6 在转型、参数声明、sizeof中定义类型
注解
在C中,转型表达式、参数声明以及sizeof表达式中都可以进行类型声明,而在C++中则不能。
示例
void func(struct TAG { int a; } st)
{
……
}
如上所示,TAG在参数声明是被定义。
准则
把在参数中声明的类型的定义式,挪到函数声明式作用域开始处,或者大于该作用域的某处。
在转型和sizeof表达式作用域开始处,或者大于该作用域的某处定义类型。
A.7 忽略局部对象定义式的控制流程跳转
注解
在C中,goto和switch语句可以使控制流程跳过块作用域内的局部对象声明式,甚至有可能是对象的初始化式。而在C++中不存在这种跳转。
示例goto LABEL;
{
int v = 0;
……
LABEL:
……
}
在C中,上面的代码是合法的,它假设标签LABEL之后的代码不依赖于整型变量v的初值0.而在C++中,这段代码总是不能通过编译。
准则
确保goto或switch语句没有跳过局部对象的初始化式。
A.8 字符数组的初始化
注解
在C中,允许使用字符串来初始化一个字符数组。初始化表达式中,数组可以比字符串少一个字符空间(‘\0’字符)。在C++中数组则必须能完全容纳字符串。
示例
char s[3] = "abc";
尽管常量字符串为4个字符大小,但数组s的大小为3.这种做法在C中合法,而在C++中非法。
准则
为了容纳‘\0’,请确保字符数组大小要比字符串长度大1.因而,有必要将字符数组大小指定为字符串长度+1.(也即:char s[4] = "abc";)
然而,为了使定义式自适应于不同的常量字符串,在定义式中不指定数组大小不失为一种好方法(即:char s[] = "abc";)。
A.9 原型声明
注解
C++程序要求在使用函数之前必须声明该函数原型。此外,C++程序会将函数声明式f()解释为f(void)——不带参数的函数。而在C中,这样的行为是不确定的。
示例
extern void func();
……
sub();
func(0);
因为没有sub函数的声明,因此调用该函数是错误的。由于声明式中函数不带参数,因此以0为参数调用func也是个错误。
准则
确保被调用的函数已经被声明。为了强调函数f不带参数,好的做法是在声明函数时,将参数声明为void,即声明式为:f(void)。
A.10 C++增加的关键字
注解
C中没有下面的C++关键字:
asm bool catch class
const_cast delete dynamic_cast explicit
false friend inline mutable
namespace new operator private
protected public reinterpret_cast
static_cast template this throw
true try typeid typename
using virtual wchar_t
示例
int class
- 最新评论
