C/C++代码风格整洁与高质量

1.变量命名:

  1)结构体变量以t开头
  2)指针以p开头
  3)临时变量以temp开头
  4)常量全用大写的字母,用下划线分割单词 const float PI = 3.14159;
  5)静态变量 s_xx
  6)全局变量 g_xx
  7)成员变量 m_xx

2. 代码长度和函数体大小

  1)长行拆分 代码行最大长度宜控制在70至80个字符以内,拆分出的新行要进行适当的缩进,使排版整齐,语句可读
  2)函数体的规模要小,尽量控制在50行代码之内

3.修饰符

  应当将修饰符 * 和 & 紧靠变量名

4.分界符‘{’和‘}’

  分界符‘{’和‘}’应独占一行并且位于同一列,同时与引用它们的语句左对齐

5.关键字之后要留空格

  例如const、virtual、inline、case 等关键字之后至少要留一个空格,否则无法辨析关键字。例如if、for、while等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。

6.标识符书写

  Windows应用程序的标识符通常采用“大小写”混排的方式,如AddChild。而Unix应用程序的标识符通常采用“小写加下划线”的方式,如add_child

7.变量的名字

  应当使用“名词”或者“形容词+名词”

8.成员函数命名

  类的成员函数应当只使用“动词”,被省略掉的名词就是对象本身

9.if语句:

  1)与零值比较的标准if语句:if (flag) // 表示flag为真 if (!flag)// 表示flag为假
  2)整型变量与零值比较:if (value == 0)
  3)浮点数比较大小,如果是<,>,可以直接写成if (a > b) ,但如果要比较=,!= ,就需要考虑精度问题,正常的思路,我们比较两个数大小是将两个数做差,与0比较,看是否等于0,因为浮点数在计算机里面表示问题(二进制无法精确表示一些浮点数),所以这里要改成两个数的差是否在一定精度范围内。

	  #define EPS 1e-6
      if(((a - b > - EPS) && ((a - b) < EPS)))

  4)指针变量与零值比较:if (NULL == p)

10. C++中尽量使用const

  在C++ 程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量,区别在于:
  1)编译器处理方式
  define –在预处理阶段进行替换
  const – 在编译时确定其值
  2)类型检查
  define – 无类型,不进行类型安全检查,可能会产生意想不到的错误
  const – 有数据类型,编译时会进行类型检查
  3)内存空间
  define – 不分配内存,给出的是立即数,有多少次使用就进行多少次替换,在内存中会有多个拷贝,消耗内存大
const – 在静态存储区中分配空间,在程序运行过程中内存中只有一个拷贝
  4)其他
  在编译时, 编译器通常不为const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
宏替换只作替换,不做计算,不做表达式求解。
宏定义的作用范围仅限于当前文件。
默认状态下,const对象只在文件内有效,当多个文件中出现了同名的const变量时,等同于在不同文件中分别定义了独立的变量。
如果想在多个文件之间共享const对象,必须在变量定义之前添加extern关键字(在声明和定义时都要加)

11.公共的头文件的使用

  为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中

12.不能在类声明中初始化const数据成员
   class A
   {
      const int SIZE = 100;   // 错误,企图在类声明中初始化const数据成员
      int array[SIZE];        // 错误,未知的SIZE
   };
13.枚举常量

  建立在整个类中都恒定的常量:用类中的枚举常量

  class A
  {
    enum { SIZE1 = 100, SIZE2 = 200}; // 枚举常量
    int array1[SIZE1]; 
    int array2[SIZE2];
  };
14.形参书写完整

  参数的书写要完整,不要贪图省事只写参数的类型而省略参数名字

    void SetValue(int width, int height);   // 良好的风格
    void SetValue(int, int);         // 不良的风格
15. const修饰输入参数

  1)如果参数作输出用,不论它是什么数据类型,也不论它采用“指针传递”还是“引用传递”,都不能加const修饰,否则该参数将失去输出功能。

  2)const只能修饰输入参数:
  如果输入参数采用“指针传递”,那么加const修饰可以防止意外地改动该指针,起到保护作用:

void StringCopy(char *strDestination,const char *strSource);

  如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const修饰

  3)void Func(A a) 这样声明的函数注定效率比较底。因为函数体内将产生A类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。
  为了提高效率,可以将函数声明改为void Func(A &a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。但是函数void Func(A &a) 存在一个缺点:“引用传递”有可能改变参数a,这是我们不期望的。解决这个问题很容易,加const修饰即可,因此函数最终成为void Func(const A &a)

  4)用const修饰函数的返回值,const关键字只能放在函数声明的尾部,该返回值只能被赋给加const修饰的同类型指针。

16. 引用、引用传递、值传递

  对于赋值函数,应当用“引用传递”的方式返回String对象;对于相加函数,应当用“值传递”的方式返回String对象。如果改用“引用传递”,那么函数返回值是一个指向局部对象temp的“引用”。由于temp在函数结束时被自动销毁,将导致返回的“引用”无效。

  引用相当于别名(绰号)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)不 能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL);一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)

17.return语句

  return不可返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁:

  char * Func(void)
  {
   	char str[] = “hello world”; // str的内存位于栈上
   	return str;     // 将导致错误
  }

  int add(int a, int b)
  {
   	int c = a+b;
   	return c; // 正常
   	return a+b; //效率更高
  }
18.函数的功能要单一,不要设计多用途的函数
19.尽量少用static局部变量

  在C/C++语言中,函数的static局部变量是函数的“记忆”存储器。建议尽量少用static局部变量,除非必需

20.断言assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况
assert((pvTo != NULL) && (pvFrom != NULL));     // 使用断言
21.内存分配

  1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量
  2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限
  3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多
程序中malloc与free的使用次数一定要相同,否则肯定有错误;
使用free或delete释放了内存后,没有将指针设置为NULL。导致产生“野指针”;
  用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存;

  malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。

   Obj  *objects = new Obj[100];   // 创建100个动态对象
   delete []objects;   // 正确的用法,注意不要漏掉[]
   delete objects; // 错误的用法,漏掉了另外99个对象
22.指针的初始化
   free(p);// p 所指的内存被释放,但是p所指的地址仍然不变

  指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。例如:

   char *p = NULL;
   char *str = (char *) malloc(100);

void Test(void)
{
  	 A  *p;
  	 {
    	A  a;
    	p = &a; // 注意 a 的生命期
	 }
	 p->Func(); // p是“野指针”
}

  函数Test在执行语句p->Func()时,对象a已经消失,而p是指向a的,所以p就成了“野指针”

23.指针的初始化

C++调用C:

extern “C”
{
  void foo(int x, int y);
  … // 其它函数
}

或者写成:

extern “C”
{
  #include “myheader.h”
  … // 其它C头文件
}
24.宏代码

  预处理器用复制宏代码的方式代替函数调用,省去了参数压栈、生成汇编语言的CALL调用、返回参数、执行return等过程,从而提高了速度。使用宏代码最大的缺点是容易出错,预处理器在复制宏代码时常常产生意想不到的边际效应。

   #define MAX(a, b)       (a) > (b) ? (a) : (b)
   result = MAX(i, j) + 2 ;

将被预处理器解释为:

   result = (i) > (j) ? (i) : (j) + 2 ;
25.inline不应该出现在函数的声明中

  关键字inline必须与函数定义体放在一起才能使函数成为内联,仅将inline放在函数声明前面不起任何作用。如下风格的函数Foo不能成为内联函数:

  inline void Foo(int x, int y); // inline仅与函数声明放在一起
  void Foo(int x, int y)
  {
      …
  }

  而如下风格的函数Foo则成为内联函数:

   void Foo(int x, int y);    
   inline void Foo(int x, int y)   // inline与函数定义体放在一起
   {
       …
   }

  以下情况不宜使用内联:
  (1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
  (2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。

  (3)类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。

26.缺省函数

  C++编译器将自动为A产生四个缺省的函数,如:

   A(void);                    // 缺省的无参数构造函数
   A(const A &a);              // 缺省的拷贝构造函数
   ~A(void);                   // 缺省的析构函数
   A & operate =(const A &a);  // 缺省的赋值函数
27.构造和析构的顺序

  构造从类层次的最根处开始,在每一层中,首先调用基类的构造函数,然后调用成员对象的构造函数。析构则严格按照与构造相反的次序执行,该次序是唯一的,否则编译器将无法自动执行析构过程。
成员对象初始化的次序完全不受它们在初始化表中次序的影响,只由成员对象在类中声明的次序决定。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浅笑一斤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值