嵌入式C++编程准则.docx
- 文档编号:24379640
- 上传时间:2023-05-26
- 格式:DOCX
- 页数:12
- 大小:21.20KB
嵌入式C++编程准则.docx
《嵌入式C++编程准则.docx》由会员分享,可在线阅读,更多相关《嵌入式C++编程准则.docx(12页珍藏版)》请在冰豆网上搜索。
嵌入式C++编程准则
嵌入式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中,这将被视为一个暂时的定义,这种定义方式在一个translationunit中允许出现多次。
示例
inta;/*
(1)*/
inta=10;/*
(2)*/
在C中,
(1)是一种暂时性的定义。
由于
(2)被看作是确切的定义,
(1)被看作是只是声明一个变量。
在C++中,
(1)、
(2)都被认为是定义式。
由于存在
(1)、
(2)两个重复定义式,因此是一个错误。
准则
在C++中,如果想在文件范围内仅声明一个对象(而不是定义该对象),那么该对象不能拥有初始化表达式,并且需要使用extern来修饰。
声明于文件范围内的每一个对象只能显式的被定义一次。
除了一次声明外,其余所有声明都必须具有extern修饰并且没有初始化表达式。
A.3const类型修饰符
注解
在C中,在文件范围内,一个使用const修饰而未指明存储类型的对象具有外部链接属性。
而在C++中,它具有内部连接属性。
示例
+-file1--------------------+
|externconstintn;|
+----------------------------+
+-file2--------------------+
|constintn=10;|
+----------------------------+
在C中,文件file2中的对象n具有外部连接属性,因此,文件file1中对象n(该对象同样具有外部链接属性)可以引用它。
在C++中,文件file2中的对象n具有的属性是内部链接的,因此,file1中的对象n无法引用到它。
准则
使用extern显示修饰const对象,使该对象具有外部链接属性。
A.4void*的转型
注解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++程序中还不能将内置的++和—以及符合运算符(如+=)作用于枚举类型变量上。
示例
enumRGB{red,green,blue}rgb;
++rgb;
如果表达式(++rgb)采用内置的++运算符,那么在C++中这是一个错误表达式。
该表达式的语意相当于:
rgb=rgb+1;
上面的表达式在C++中还是错误的。
而像下面这样,对枚举值进行显示的转换就是正确的:
rgb=RGB(rgb+1);
最好的解决方法是为枚举类型RGB实现一个++运算符。
RBG&operator++(RGB&x)
{
returnx=RGB(x+1);
}
准则
将C程序移植为C++程序时,在需要的时候,要为枚举类型实现类型安全的++和——操作符。
A.6在转型、参数声明、sizeof中定义类型
注解
在C中,转型表达式、参数声明以及sizeof表达式中都可以进行类型声明,而在C++中则不能。
示例
voidfunc(structTAG{inta;}st)
{
……
}
如上所示,TAG在参数声明是被定义。
准则
把在参数中声明的类型的定义式,挪到函数声明式作用域开始处,或者大于该作用域的某处。
在转型和sizeof表达式作用域开始处,或者大于该作用域的某处定义类型。
A.7忽略局部对象定义式的控制流程跳转
注解
在C中,goto和switch语句可以使控制流程跳过块作用域内的局部对象声明式,甚至有可能是对象的初始化式。
而在C++中不存在这种跳转。
示例gotoLABEL;
{
intv=0;
……
LABEL:
……
}
在C中,上面的代码是合法的,它假设标签LABEL之后的代码不依赖于整型变量v的初值0.而在C++中,这段代码总是不能通过编译。
准则
确保goto或switch语句没有跳过局部对象的初始化式。
A.8字符数组的初始化
注解
在C中,允许使用字符串来初始化一个字符数组。
初始化表达式中,数组可以比字符串少一个字符空间(‘\0’字符)。
在C++中数组则必须能完全容纳字符串。
示例
chars[3]="abc";
尽管常量字符串为4个字符大小,但数组s的大小为3.这种做法在C中合法,而在C++中非法。
准则
为了容纳‘\0’,请确保字符数组大小要比字符串长度大1.因而,有必要将字符数组大小指定为字符串长度+1.(也即:
chars[4]="abc";)
然而,为了使定义式自适应于不同的常量字符串,在定义式中不指定数组大小不失为一种好方法(即:
chars[]="abc";)。
A.9原型声明
注解
C++程序要求在使用函数之前必须声明该函数原型。
此外,C++程序会将函数声明式f()解释为f(void)——不带参数的函数。
而在C中,这样的行为是不确定的。
示例
externvoidfunc();
……
sub();
func(0);
因为没有sub函数的声明,因此调用该函数是错误的。
由于声明式中函数不带参数,因此以0为参数调用func也是个错误。
准则
确保被调用的函数已经被声明。
为了强调函数f不带参数,好的做法是在声明函数时,将参数声明为void,即声明式为:
f(void)。
A.10C++增加的关键字
注解
C中没有下面的C++关键字:
asmboolcatchclass
const_castdeletedynamic_castexplicit
falsefriendinlinemutable
namespacenewoperatorprivate
protectedpublicreinterpret_cast
static_casttemplatethisthrow
truetrytypeidtypename
usingvirtualwchar_t
示例
intclass,new,old;
准则
确保没有使用C++关键字作为标记符。
A.11嵌套类型的作用域
注解
C结构体或联合体内定义嵌套类型,该类型的作用域终止点和该结构体或联合体相同。
而C++中定义的嵌套类型作用域终结于该结构或联合体。
示例
structS{
inta;
structT{
intt;
}b;
intc;
enumE{V1,V2}e;
};
structTx;
enumEy;
x、y的声明在C程序中是合法的,但在C++中非法。
在C++程序中,在类S中定义的类型T、E的作用域不会超过类S的定义范围。
准则
除非只是在结构体或联合体内使用定义的嵌套类型,否则不在嵌套作用域内定义类型。
A.12动态内存管理
注解new/delete和malloc/free没有采用相同的内存管理策略。
因此,除非已经使用new获取了内存,否则应用程序中不能使用delete来释放内存。
同样,除非使用了malloc分配内存,否则不能使用free来释放内存。
示例
int(*p)[10];
p=(int(*)[10])malloc(sizeof(*p));
....
deletep;
这里的delete具有未定义的行为。
准则
在C++中避免使用malloc/calloc/realloc/free函数,而仅使用new/delete.
A.13'/'之后的'/*'
注解
C++程序具有‘//’的注释风格,因此紧接在符号‘/’之后的C风格注释‘/**/’,会被C++程序作为理解为‘//’后接‘**/’。
示例
i=j//*comment*/k;
‘//’被解释为注释分隔符,因而表达式被解释为‘i=j’而不是‘i=j/k’。
准则
尽量避免紧接着符号‘/’后写C风格注释‘/**/’。
B.关于代码容量的准则
B.1对象初始化
注解
有很多方法来初始化某个对象,有些初始化方法将会产生不必要的临时对象,从而导致代码量膨胀。
例如:
Tx(i)//
(1)
Tx=i;//
(2)
Tx=T(i)//(3)
Tx;//(4)
x=i;//
(1)直接使用构造函数来初始化对象x,这样就不会产生临时对象。
调用形式类似于:
x.T(i);//x对象调用构造函数
(2)在某些实现中,方法
(2)类似于方法
(1),即对象x直接调用
构造函数。
而在另外一些实现中,该方法会先使用构造函数生成一个临时对象,然后将该临时对象作为对象x的初始值。
调用形式类似于:
temp.T(i);//生成temp
x.T(temp);//x调用拷贝构造函数
temp.~T();//析构temp
(3)等同于
(2)
(4)使用T的默认构造函数来初始化x,然后调用赋值操作符将新值赋给x.赋值操作符可能会释放x正在使用的资源,并重新为x获取新的资源。
x.T();//x调用默认构造函数
x.operator=(i);//x调用赋值操作符
准则
在上面的四种方法中,优先考虑方法
(1)。
B.2inline标记符
注解
内联减少了函数进出栈上的管理开销,但这也同时增加了代码容量。
在内内部定义的成员函数默认是内联的。
准则
仅对小函数进行inline修饰。
在类体之外定义所有不适合作为内联函数的成员方法,从而使得该成员不是内联函数。
B.3返回值中的临时对象
注解
函数按值返回一个对象时,可能需要创建并销毁一个临时对象,从而导致代码量增加并且带来运行时开销。
示例
classMatrix{
inta,b;
public:
Matrix&operator+=(constMatrix&);
friend
Matrixoperator+(constMatrix&,constMatrix&);
};
Matrixoperator+(constMatrix&,constMatrix&)
{
...
}
voidfunc()
{
Matrixa,b;
a=a+b;//
(1)
a+=b;//
(2)
}
函数func在
(1)处调用了+操作符,该操作符按值返回一个Matrix对象。
在某些编译器实现中,会先构造一个Maxtrix临时对象,然后销毁该临时对象。
函数func在
(2)处调用+=操作符,该操作符返回一个Matrix对象引用,因而不会构造临时对象。
准则
对于类类型对象,使用复合赋值操作符(如使用‘+=’而不是‘+’和‘=’)来避免编译器生成再销毁不必要的临时对象。
注:
个人认为这个准则有失准确。
此例中,+=操作符不产生临时对象的原因在于该操作符按引用返回对象而非按值返回对象。
因此,准确的说法是,使用按引用或指针返回对象的函数来避免构造不必要的临时对象。
B.4new和delete操作符
准则
在必要时,实现类属的new和delete操作符,从而提升管理动态内存的速度和内存利用率。
B.5全局对象的初始化
注解
全局对象初始化的顺序依赖于编译器的实现。
但可以肯定的是,在同一个解释单元其初始化顺序和对象的声明顺序相同。
示例
文件1文件2文件3
inta=f();intb=f();intf(void)
{
staticinta=0;
returna++;
}
程序可能将a初始化为0,而b为1,或者反过来。
这依赖于编译器如何选择他们的初始化顺序。
如果将文件2中变量b的声明移到文件1中,那么两变量的初始化顺序就随即确定了,即:
文件1文件2文件3
inta=f();intf(void)
intb=f();{
staticinta=0;
returna++;
}
这种情况下,a要先于b被初始化。
避免编写依赖于不同解释单元内全局对象初始化顺序的代码。
C.关于速度的准则
C.1元素为类对象的数组中的new/delete
注解
声明元素为类对象的数组时,编译器会为该数组每一个元素调用构造函数。
在超出该数组的作用域范围时,又会一一调用每个元素的析构函数。
构造/析构可能占用超乎想象的时间,这在实时过程系统中是一个问题。
准则
在对时间要求比较高的过程系统中,避免创建/销毁大型的元素为类对象的数组。
C.2循环体内的对象声明
注解
如果在循环体内声明类变量,那么在循环的每次迭代时都需要构造并析构该对象。
这种构造并析构带来的问题就是循环执行速度降低。
示例
for(i=0;i<1000;i++)
{
FOOa;
...
}
准则在循环体外部声明类类型变量,而避免在内部声明。
D.编写只读型代码准则
D.1ROM中的const对象
注解
通常,如果const对象具有如下一些属性,则可以存储在ROM中:
——具有static存储时长
——由常量表达式初始化
——是一个POD(plainolddata)类型对象
POD类型对象具有如下属性:
——一个无属性(scalar)类型(数字,枚举和指针)
——类/结构/联合体中所有数据成员都是public访问权限并且是POD类型,同时类中没有用户自定义的构造函数/析构函数,没有基类和虚函数
——所有元素都是POD类型的数组
示例
staticconstcharlang[]="EC++";
classA{
inta;
public:
A();
~A();
};
constAx;
'lang'可能存储于ROM中,而‘x’则不会。
准则
欲将对象存储于ROM中,则将对象声明为POD类型,并使用常量初始化该对象。
NOTE:
Theformofpresentationusedhere,andseveralofthespecificguidelines,wereinspiredbytheexcellentbookbyThomasPlumandDanSaks,'C++ProgrammingGuidelines'(PlumHallInc.,1991)。
译自:
原文链接:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 嵌入式 C+ 编程 准则
![提示](https://static.bdocx.com/images/bang_tan.gif)