zoukankan      html  css  js  c++  java
  • 高质量编程指南 难点记录

    1.头文件规范:
    如:

    RB-Tree的插入和删除操作的实现算法
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  1.版权声明:
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME    Copyright @ 2006,pengkuny( http://www.cppblog.com/pengkuny
    ),All rights reserved.
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME    您可以自由的传播,修改这份代码,转载处请注明原作者 
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  2.文件名称: 
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  3.参考资料:
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  1) <<Introduction to algorithm>>
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME
      2) 
    http://www.ececs.uc.edu/ ~franco/C321/html/RedBlack/redblack.html
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  4.内容摘要:
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  红黑树的几个性质:
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  1) 每个结点只有红和黑两种颜色
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  2) 根结点是黑色的
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  3) 每个叶子结点(空结点被认为是叶子结点)是黑色的
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  4) 如果一个结点是红色的,那么它的左右两个子结点的颜色是黑色的
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  5) 对于每个结点而言,从这个结点到叶子结点的任何路径上的黑色结点
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  的数目相同 
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  5.完成日期:
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME    2006.11.14

    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME  -------------------------------------------------------------
    */   

    2.【规则】一行代码只做一件事情,如只定义一个变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。如:

    int width; // 宽度
    int height; // 高度
    int depth; // 深度
    x = a + b;
    y = c + d;
    z = e + f;
    if (width < height)
    {
    dosomething();
    }
    for (initialization; condition; update)
    {
    dosomething();
    }
    // 空行
    other();

    3.【建议】尽可能在定义变量的同时初始化该变量(就近原则)
    如:

    int  width  =   10 ;   //  定义并初绐化width 
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME
    int  height  =   10 //  定义并初绐化height 
    高质量编程指南 难点记录 - 魑魅魍魉福 - 魑魅魍魉HOME
    int  depth  =   10 ;   //  定义并初绐化depth 

    4.空格写法:
     【规则】if、for、while等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。

     【规则】函数名之后不要留空格,紧跟左括号‘(’,以与关键字区别。

     【规则】‘(’向后紧跟,‘)’、‘,’、‘;’向前紧跟,紧跟处不留空格。

     【规则】‘,’之后要留空格,如Function(x, y, z)。如果‘;’不是一行的结束符号,其后要留空格,如for (initialization; condition; update)。

     【规则】赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如“=”、“+=” “>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格。

     【规则】一元操作符如“!”、“~”、“++”、“--”、“&”(地址运算符)等前后不加空格。

    5.【建议】对于表达式比较长的for语句和if语句,为了紧凑起见可以适当地去掉一些空格,如for (i=0; i<10; i++)和if ((a<=b) && (c<=d))

    6.长行拆分:
     【规则】长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。

    7.应当将修饰符 * 和 & 紧靠变量名
    例如:
    char  *name;
    int   *x, y; // 此处y不会被误解为指针

    8.注释的写法:
       程序块的注释常采用“/*…*/”,行注释一般采用“//…”。

     【规则】注释是对代码的“提示”,而不是文档。程序中的注释不可喧宾夺主,注释太多了会让人眼花缭乱。注释的花样要少。

     【规则】边写代码边注释

     【规则】注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不可放在下方。

    9.类的版式:
       我们不可以滥用类的封装功能,不要把它当成火锅,什么东西都往里扔。
       将public类型的函数写在前面,而将private类型的数据写在后面,“以行为为中心”,重点关注的是类应该提供什么样的接口(或服务)。

     10 目录结构

    如果一个软件的头文件数目比较多(如超过十个),通常应将头文件和定义文件分别
    保存于不同的目录,以便于维护。
    例如可将头文件保存于include 目录,将定义文件保存于source 目录(可以是多级
    目录)。
    如果某些头文件是私有的,它不会被用户的程序直接引用,则没有必要公开其“声
    明”。为了加强信息隐藏,这些私有的头文件可以和定义文件存放于同一个目录。

    11命名规则

    命名规则尽量与所采用的操作系统或开发工具的风格保持一致。
    例如Windows 应用程序的标识符通常采用“大小写”混排的方式,如AddChild。而
    Unix 应用程序的标识符通常采用“小写加下划线”的方式,如add_child。别把这两类风
    化境编程界推荐图书系列 - 高质量C++/C 编程指南,v 1.0
    2001 Page 23 of 101
    格混在一起用。

    【建议3-1-1】尽量避免名字中出现数字编号,如Value1,Value2 等,除非逻辑上的
    确需要编号。这是为了防止程序员偷懒,不肯为命名动脑筋而导致产生无意义的名
    字(因为用数字编号最省事)。

    12  简单的Windows 应用程序命名规则
    作者对“匈牙利”命名规则做了合理的简化,下述的命名规则简单易用,比较适合
    于Windows 应用软件的开发。

    z 【规则3-2-1】类名和函数名用大写字母开头的单词组合而成。
    例如:
    class Node; // 类名
    class LeafNode; // 类名
    void Draw(void); // 函数名
    void SetValue(int value); // 函数名
    z 【规则3-2-2】变量和参数用小写字母开头的单词组合而成。
    例如:
    BOOL flag;
    int drawMode;
    z 【规则3-2-3】常量全用大写的字母,用下划线分割单词。
    例如:
    const int MAX = 100;
    const int MAX_LENGTH = 100;
    z 【规则3-2-4】静态变量加前缀s_(表示static)。
    例如:
    void Init(…)
    {
    static int s_initValue; // 静态变量

    }
    z 【规则3-2-5】如果不得已需要全局变量,则使全局变量加前缀g_(表示global)。
    例如:
    int g_howManyPeople; // 全局变量
    int g_howMuchMoney; // 全局变量
    z 【规则3-2-6】类的数据成员加前缀m_(表示member),这样可以避免数据成员与
    成员函数的参数同名。
    例如:
    void Object::SetValue(int width, int height)
    {
    m_width = width;
    m_height = height;
    }
    z 【规则3-2-7】为了防止某一软件库中的一些标识符和其它软件库中的冲突,可以为
    各种标识符加上能反映软件性质的前缀。例如三维图形标准OpenGL 的所有库函数
    均以gl 开头,所有常量(或宏定义)均以GL 开头。

    13.布尔变量与零值比较:
    【规则1】不可将布尔变量直接与TRUE、FALSE或者1、0进行比较。
        根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。TRUE的值究竟是什么并没有统一的标准。例如Visual C++ 将TRUE定义为1,而Visual Basic则将TRUE定义为-1。
    假设布尔变量名字为flag,它与零值比较的标准if语句如下:
    if (flag) // 表示flag为真
    if (!flag) // 表示flag为假
    其它的用法都属于不良风格,例如:
     if (flag == TRUE) 
     if (flag == 1 )  
     if (flag == FALSE) 
     if (flag == 0) 

    对于林锐提出的规则我有质疑,既然bool型的值按定义记为FALSE和TRUE,
    那么我根本不关心FALSE和TRUE的具体值,那么
     if (flag == TRUE)  
    又有何不可?系统总不会把TRUE定义为0吧?(如果是这样,林锐还是对的)

    14.整型变量与零值比较:
    【规则】应当将整型变量用“==”或“!=”直接与0比较。
    与零值比较的标准if语句如下:
    if (value == 0) 
    if (value != 0)
    不可模仿布尔变量的风格而写成
    if (value)  // 会让人误解 value是布尔变量
    if (!value)

    15.浮点变量与零值比较:
    if ((x>=-EPSINON) && (x<=EPSINON))

    16.指针变量与零值比较:
    if (p == NULL) // p与NULL显式比较,强调p是指针变量
    if (p != NULL) 


    为了防止将 if (p == NULL) 误写成 if (p = NULL),而有意把p和NULL颠倒。
    编译器认为 if (p = NULL) 是合法的,但是会指出 if (NULL = p)是错误的,
    因为NULL不能被赋值。

    17 if (condition) //不良风格,容易出错
     return x;
    return y;
    改写为
    if (condition)
    {
     return x;
    }
    else
    {
     return y;
    }
    或者改写成更加简练的
    return (condition ? x : y);

    18 for

    【建议4-4-1】在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的
    循环放在最外层,以减少CPU 跨切循环层的次数。例如示例4-4(b)的效率比示例
    4-4(a)的高。

    【建议4-4-2】如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到
    循环体的外面。示例4-4(c)的程序比示例4-4(d)多执行了N-1 次逻辑判断。并且由
    于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进
    行优化处理,降低了效率。如果N 非常大,最好采用示例4-4(d)的写法,可以提高
    效率。如果N 非常小,两者效率差别并不明显,采用示例4-4(c)的写法比较好,因
    为程序更加简洁。

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

    19 goto

    goto 语句至少有一处可显神通,它能从多重循环体中咻
    地一下子跳到外面,用不着写很多次的break 语句;

    20   const 与 #define 的比较
    C++ 语言可以用const 来定义常量,也可以用 #define 来定义常量。但是前者比后
    者有更多的优点:
    (1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安
    全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会
    产生意料不到的错误(边际效应)。
    (2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
    z 【规则5-2-1】在C++ 程序中只使用const 常量而不使用宏常量,即const 常量完
    全取代宏常量。

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

    21 类中使用const常量:
    1)不能在类声明中初始化const数据成员。
      因为类的对象未被创建时,编译器不知道该成员的值是什么。
      const数据成员的初始化只能在类构造函数的初始化表中进行.
    2)const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,
      因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。
    3)怎样才能建立在整个类中都恒定的常量呢?
      别指望const数据成员了,应该用类中的枚举常量来实现。
      但是缺点很明显,只能表示有限大小的整型值.

    22 参数的规则

    【规则6-1-1】参数的书写要完整,不要贪图省事只写参数的类型而省略参数名字。
    如果函数没有参数,则用void 填充。
    例如:
    void SetValue(int width, int height); // 良好的风格
    void SetValue(int, int); // 不良的风格
    float GetValue(void); // 良好的风格
    float GetValue(); // 不良的风格

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

    对于赋值函数,应当用“引用传递”的方式返回String 对象。如果用“值传递”的
    方式,虽然功能仍然正确,但由于return 语句要把 *this 拷贝到保存返回值的外部存储
    单元之中,增加了不必要的开销,降低了赋值函数的效率

    23 return

    (1)return 语句不可返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数
    体结束时被自动销毁。例如
    char * Func(void)
    {
    char str[] = “hello world”; // str 的内存位于栈上

    return str; // 将导致错误
    }
    (2)要搞清楚返回的究竟是“值”、“指针”还是“引用”。
    (3)如果函数返回值是一个对象,要考虑return 语句的效率。例如
    return String(s1 + s2);
    这是临时对象的语法,表示“创建一个临时对象并返回它”。不要以为它与“先创建
    一个局部对象temp 并返回它的结果”是等价的,如
    String temp(s1 + s2);
    return temp;
    实质不然,上述代码将发生三件事。首先,temp 对象被创建,同时完成初始化;然
    后拷贝构造函数把temp 拷贝到保存返回值的外部存储单元中;最后,temp 在函数结束
    时被销毁(调用析构函数)。然而“创建一个临时对象并返回它”的过程是不同的,编译
    器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的化费,提高了
    效率。

    24 assert

    断言assert 是仅在Debug 版本起作用的宏,它用于检查“不应该”发生的情况。示
    例6-5 是一个内存复制函数。在运行过程中,如果assert 的参数为假,那么程序就会中
    止(一般地还会出现提示对话,说明在什么地方引发了assert)。

    【规则6-5-1】使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况
    之间的区别,后者是必然存在的并且是一定要作出处理的。
    z 【规则6-5-2】在函数的入口处,使用断言检查参数的有效性(合法性)。
    z 【建议6-5-1】在编写函数时,要进行反复的考查,并且自问:“我打算做哪些假定?”
    一旦确定了的假定,就要使用断言对假定进行检查。
    z 【建议6-5-2】一般教科书都鼓励程序员们进行防错设计,但要记住这种编程风格可
    能会隐瞒错误。当进行防错设计时,如果“不可能发生”的事情的确发生了,则要
    使用断言进行报警。

     24  内存

    内存分配方式有三种:
    (1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的
    整个运行期间都存在。例如全局变量,static 变量。
    (2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函
    数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集
    中,效率很高,但是分配的内存容量有限。
    (3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多
    少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期
    由我们决定,使用非常灵活,但问题也最多。

    【规则7-2-1】用malloc 或new 申请内存之后,应该立即检查指针值是否为NULL。
    防止使用指针值为NULL 的内存。
    z 【规则7-2-2】不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右
    值使用。
    z 【规则7-2-3】避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”
    操作。
    z 【规则7-2-4】动态内存的申请与释放必须配对,防止内存泄漏。
    z 【规则7-2-5】用free 或delete 释放了内存之后,立即将指针设置为NULL,防止产
    生“野指针”。

    25 计算内存容量
    用运算符sizeof 可以计算出数组的容量(字节数)。示例7-3-3(a)中,sizeof(a)
    的值是12(注意别忘了’\0’)。指针p 指向a,但是sizeof(p)的值却是4。这是因为
    sizeof(p)得到的是一个指针变量的字节数,相当于sizeof(char*),而不是p 所指的内
    存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。

    注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。示例
    7-3-3(b)中,不论数组a 的容量是多少,sizeof(a)始终等于sizeof(char *)

    26 free 和delete

    别看free 和delete 的名字恶狠狠的(尤其是delete),它们只是把指针所指的内存给
    释放掉,但并没有把指针本身干掉。
    用调试器跟踪示例7-5,发现指针p 被free 以后其地址仍然不变(非NULL),只是
    该地址对应的内存是垃圾,p 成了“野指针”。如果此时不把p 设置为NULL,会让人误
    以为p 是个合法的指针。
    如果程序比较长,我们有时记不住p 所指的内存是否已经被释放,在继续使用p 之
    前,通常会用语句if (p != NULL)进行防错处理。很遗憾,此时if 语句起不到防错作用,
    因为即便p 不是NULL 指针,它也不指向合法的内存块。

    malloc 与free 是C++/C 语言的标准库函数,new/delete 是C++的运算符。它们都可
    用于申请动态内存和释放内存。
    对于非内部数据类型的对象而言,光用maloc/free 无法满足动态对象的要求。对象
    在创建的同时要自动执行构造函数, 对象在消亡之前要自动执行析构函数。由于
    malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数
    和析构函数的任务强加于malloc/free。
    因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个
    能完成清理与释放内存工作的运算符delete。注意new/delete 不是库函数。

    所以我们不要企图用malloc/free 来完成动态对象的内存管理,应该用new/delete。
    由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free 和new/delete
    是等价的。
    既然new/delete 的功能完全覆盖了malloc/free,为什么C++不把malloc/free 淘
    汰出局呢?这是因为C++程序经常要调用C 函数,而C 程序只能用malloc/free 管理动
    态内存。
    如果用free 释放“new 创建的动态对象”,那么该对象因无法执行析构函数而可能
    导致程序出错。如果用delete 释放“malloc 申请的动态内存”,理论上讲程序不会出错,
    但是该程序的可读性很差。所以new/delete 必须配对使用,malloc/free 也一样。

    不行。如果发生“内存耗尽”这样的事情,一般说来应用程序已经无药可救。如果
    不用exit(1) 把坏程序杀死,它可能会害死操作系统。道理如同:如果不把歹徒击毙,歹
    徒在老死之前会犯下更多的罪。

    有一个很重要的现象要告诉大家。对于32 位以上的应用程序而言,无论怎样使用
    malloc 与new,几乎不可能导致“内存耗尽”。我在Windows 98 下用Visual C++编写了
    测试程序,见示例7-9。这个程序会无休止地运行下去,根本不会终止。因为32 位操作
    系统支持“虚存”,内存用完了,自动用硬盘空间顶替

    27 malloc/free 的使用要点
    函数malloc 的原型如下:
    void * malloc(size_t size);
    用malloc 申请一块长度为length 的整数类型的内存,程序如下:
    int *p = (int *) malloc(sizeof(int) * length);

    如果p 是NULL 指针,
    那么free 对p 无论操作多少次都不会出问题。如果p 不是NULL 指针,那么free 对p
    连续操作两次就会导致程序运行错误。

    28  new/delete 的使用要点
    运算符new 使用起来要比函数malloc 简单得多,例如:
    int *p1 = (int *)malloc(sizeof(int) * length);
    int *p2 = new int[length];
    这是因为new 内置了sizeof、类型转换和类型安全检查功能

    在用delete 释放对象数组时,留意不要丢了符号‘[]’。例如
    delete []objects; // 正确的用法
    delete objects; // 错误的用法
    后者相当于delete objects[0],漏掉了另外99 个对象。

  • 相关阅读:
    欧几里得算法&&扩展欧几里得算法
    POJ-1006 Biorhythms (生物节律)
    第3周实践项目7 删除链表元素最大值
    第3周实践项目1 顺序表的基本运算
    第三周项目4(2)-顺序表应用 将所有奇数移到所有偶数前面
    第3周实践项目5 -顺序表的应用 拆分单链表
    SDUT-2144 图结构练习——最小生成树
    SDUT-3362 数据结构实验之图论六:村村通公路
    Codeforces Round #616 (Div. 2)题解
    1.29 educational round 81
  • 原文地址:https://www.cnblogs.com/zhihaowang/p/10128748.html
Copyright © 2011-2022 走看看