笔记20-30

来源:互联网 发布:淘宝不同店铺重复铺货 编辑:程序博客网 时间:2024/04/29 18:43

21. 文件结构

   .cpp.h文件两部分构成。在头文件中包括:版权版本申明等整体信息、预编译文件

   函数和结构体的声明。不要在头文件中存放定义。源文件主要包括:版权和版本声明等

   文件信息、包含头文件操作、源程序。注意在头文件中声明的函数可以在主函数中直接    

   调用,而被调函数的实现可以在另一个.cpp文件中实现。在函数实现的.cpp文件中也要

   包含该源文件编译通过的所需的头文件。

   为了保证不重复定义,每一个头文件都应该用这样的形式来完成:#ifndef/#define/#endif  

   结构产生预处理块。

22. 编写程序的风格

  空行:空行可以起到分割段落的作用,要舍得用空行,但不要空太多。每个函数定义结束 

        之后都要加空行。一个函数体内,逻揖上密切相关的语句之间不加空行,其它地

        方应加空行分隔

  代码行:一行代码只做一件事,不能把许多代码放到一行。ifforwhiledo等语句自

        占一行,执行语句不得紧跟其后。不论执行的语句有多少都要加{}隔开。尽可能

        在定义变量的同时初始化该变量。

  代码行内的空格:关键字之后要留空格。象constvirtualinlinecase 等关键字之后至            

        少要留一个空格,否则无法辨析关键字。象ifforwhile等关键字之后应留一个   

        空格再跟左括号,以突出关键字;函数名之后不要留空格,紧跟左括号  

      ‘,以与关键字区别;(向后紧跟,;向前紧跟,紧跟  

       处不留空格;之后要留空格,如Function(x, y, z)。如果;不是一行的结束

       符号,其后要留空格,如for (initialization; condition; update);二元操作

       符前后加空格,一元操作符前后不加空格;象[].->这类操作

       符前后不加空格。对于表达式比较长的for语句和if语句,为了紧凑起见可以适当地

     去掉一些空格,如for (i=0; i<10; i++)和if ((a<=b) && (c<=d)) for (i = 0; I < 

     10; i ++) // 过多的空格

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

  长行的拆分:一行最多80个字符,太多了要进行拆分。拆分要在低优先级的操作符处进行,拆分后的操

    作符位于下一行的行首,并且要进行适当的缩进 

  修饰符的位置:*&的位置应该靠近变量而不是数据类型,并且后边没有空格。

  注释:C语言的注释符为/*…*/C++中也可以用//来注释一行。注释不是文档,不能  

      太啰嗦,只在关键的函数接口,重要的行注释。注释的位置应该在上或右边,不可放

      在下方

23. 命名的规则

A•标识符最好采用英文单词或其组合,便于记忆和阅读。切忌使用汉语拼音来命名。

    A•标识符的长度应当符合“min-length && max-information”原则。 

    A•命名规则尽量与所采用的操作系统或开发工具的风格保持一致。例如Windows应用程序的标识符通常采用“大小写”混排的方式,如AddChild。而Unix应用程序的标识符通常采用“小写加下划线”的方式,如add_child。别把这两类风格混在一起用。 

    A•程序中不要出现仅靠大小写区分的相似的标识符。例如:int x, X; // 变量x 与 X   

    A•程序中不要出现标识符完全相同的局部变量和全局变量,尽管两者的作用域不同而不会发生语法错误,但会使人误解。

    A•变量的名字应当使用名词或者形容词+名词

    A•全局函数的名字应当使用动词或者动词+名词(动宾词组)。类的成员函数应当只使用动词,被省略掉的名词就是对象本身。例如: 

DrawBox(); // 全局函数; box->Draw(); // 类的成员函数;

    A•尽量避免名字中出现数字编号,如Value1,Value2等

    

    BWindows应用程序命名规则

  •类名和函数名用大写字母开头的单词组合而成。 变量和参数用小写字母开头的单词组合而成。常量全用大写的字母,用下划线分割单词。静态变量加前缀s_(表示static)。

如果不得已需要全局变量,则使全局变量加前缀g_(表示global)。

    •类的数据成员加前缀m_(表示member),这样可以避免数据成员与成员函数的参数

同名。

•变量命名细则:aName//数组

               bName//bool型的变量

               hName//句柄

               m_Name//类的数据成员

               iName//int型变量

               lName//长整型

                   fName//float型变量

                   chName//char型变量

                   sName//字符串变量

                   nName//int型正整数

                   npName//指针变量

                   lpName//远指针变量

                   dName//double型变量

                   

24. 运算符的优先级

    

    我们不可能每次都准确的记住每个优先级,所以在多个运算符号同时存在是要注意用

    ()来增加程序的可读性。

25. if语句

    •不可将布尔变量直接与TRUEFALSE或者10进行比较。而应该用if(!bFlag)

    •应当将整型变量用==!=直接与0比较。 

    •不可将浮点变量用==!=与任何数字比较。 

    •应当将指针变量用==!=与NULL比较。 

26. 循环

    •在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。

    •如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。

    •不可在for 循环体内修改循环变量,防止for 循环失去控制。

    •建议for语句的循环控制变量的取值采用半开半闭区间写法。 

27. 常量

   C++ 语言除了 #define外还可以用const来定义常量。const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。 

   需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部。为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。

28函数传递参数的规则

   ●参数要写名字,不能只写参数的类型。没有参数的函数的参数要写void,不能空着。

   ●参数的命名要恰当,不要出现以数字区分参数的情形;参数的顺序要合理,一般地,应将目的参数放在前面,源参数放在后面。

   ●如果参数是指针,且仅作输入用,则应在类型前加 const,以防止该指针在函数体内被意外修改。 

   ●如果输入参数以值传递的方式传递对象,则宜改用“const &”方式来传递,这样可以省去临时对象的构造和析构过程,从而提高效率。

   ●避免函数有太多的参数,参数个数尽量控制在 个以内。如果参数太多,在使用时容易将参数类型或顺序搞错。参数太多时要把多个参数封装到一个结构体中,在参数中传递这个结构体对象的一个指针就可。

29.函数返回值的规则

   ●不要省略返回值的类型。没有返回值时用void

   ●函数名字与返回值类型在语义上不可冲突。典型的反面教材是getchar函数,本来应该返回一个char型的变量,但实际上返回的是一个int型的变量。如果我们:

      char chName; 

      chName = getchar();

      if  (chName  = =  EOF) 

      … 

由于char类型的返回值的范围是-128~127,如果EOF在定义时超过这个范围,那么就永远也不会执行if下的那段程序了。

函数内部实现的规则

   ●在函数的入口处,记得检查输入参数的合法性,这样可以减少绝大部分的错误。

   ●return 语句不可返回指向栈内存指针或者引用,因为该内存在函数体结束时被自动销毁。例如:

   char * Func(void) 

 { 

    char str[] = “hello world”;  // str 的内存位于栈上 

   … 

    return str;    // 将导致错误 

 }

   ●如果函数返回值是一个对象,要考虑 return 语句的效率。

   ●尽量避免函数带有记忆功能。相同的输入应当产生相同的输出。

   ●不仅要检查输入参数的有效性,还要检查通过其它途径进入函数体内的变量的有效性,例如全局变量、文件句柄等。特别时全局变量。

内存的分配方式

   ●从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的

整个运行期间都存在。例如全局变量,static 变量。

   ●在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函

数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集

中,效率很高,但是分配的内存容量有限。 

   ●从堆上分配,亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意多

少的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期

由我们决定,使用非常灵活,但问题也最多。

30.常见的内存错误

   ●内存分配未成功,却使用了它。用 malloc或 new申请内存之后,应该立即检查指针值是否为 NULL。防止使用指针值为 NULL的内存。

   ●内存分配虽然成功,但是尚未初始化就引用它。不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。

   ●内存分配成功并且已经初始化,但操作越过了内存的边界。避免数组或指针的下标越界,特别要当心发生多 1”或者少 1”操作。

   ●忘记了释放内存,造成内存泄露。动态内存的申请与释放必须配对,防止内存泄漏。

   ●释放了内存却继续使用它。例如,使用 free 或 delete 释放了内存后,没有将指针设置为 NULL。导致产生野指针。因此,用 free 或 delete 释放了内存之后,立即将指针设置为 NULL,防止产生野指针。

指针与数组的区别

   ●数组要么在静态存储区被创建(如全局数组) , 要么在栈上被创建。数组名对应着 (而

不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。 指针可以随时指向任意类型的内存块,它的特征是可变” ,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。

   ●修改内容:

     char a[] = “hello”; //a的内容可变,但a不可以再指向其他的内存区域

     a[0] = ‘X’; 

     cout << a << endl; 

     char *p = “world”;  // 注意 指向常量字符串 ,p的指向可变,但内容不可修改了。

     p[0] = ‘X’;            // 编译器不能发现该错误 

     cout << p << endl; 

   ●内容的复制与比较:

     / / 数组… 

     char a[] = "hello"; 

     char b[10]; 

     strcpy(b, a);      // 不能用  b = a; 

     if(strcmp(b, a) == 0)  // 不能用  if (b == a)

     // 指针… 

     int len = strlen(a); 

     char *p = (char *)malloc(sizeof(char)*(len+1)); 

     strcpy(p,a);      // 不要用 p = a; 

     if(strcmp(p, a) == 0)  // 不要用 if (p == a) 

   ●内存容量的计算:

     char a[] = "hello world"; 

     char *p  = a; 

     cout<< sizeof(a) << endl;  // 12 字节 ,不要忘记最后的/0

     cout<< sizeof(p) << endl;  // 4 字节,计算的是sizeof(char *),而不是指针指向的内存空间

     void Func(char a[100]) 

     { 

        cout<< sizeof(a) << endl;  // 4 字节而不是 100 字节 。数组作为参数时自动退化为该                

                               //类型的指针

     }