《高质量C++&C 编程指南》读书笔记

高质量C++/C 编程指南

作者:林锐

 

前言

一、编程老手与高手的误区

进度表

使用说明书

注释

流程图

参考手册

文档

用户更明白用户需要什么

团队开发的理念

第一次就正确运行

上午9:00 到下午5:00 之间工作

 

软件设计的基础知识

 

提高质量生产率是软件工程要解决的核心问题

 

写函数strcpy 的代码:

这么一个小不点的函数,他从三个方面考查:

(1)编程风格;

(2)出错处理;

(3)算法复杂度分析(用于提高性能)

 

二、本书导读

 

二、本书导读

本书第一章至第六章主要论述C++/C 编程风格。难度不高,但是细节比较多。别小

看了,提高质量就是要从这些点点滴滴做起。

 

第七章至第十一章是专题论述,技术难度比较高,看书时要积极思考。特别是第七

章“内存管理”,读了并不表示懂了,懂了并不表示就能正确使用。

 

建议大家阅读本书的参考文献,那些都是经典名著。

 

试问有多少软件开发人员对正确性、健壮性、可靠性、效率、易用性、可读性(可理解性)、可扩展性、

可复用性、兼容性、可移植性等质量属性了如指掌?并且能在实践中运用自如?。

 

如果你的编程质量已经过关了,不要就此满足。如果你想成为优秀的软件开发人员,

建议你阅读并按照CMMI 规范做事,让自己的综合水平上升一个台阶。

 

1 章文件结构

 

版权和版本的声明位于头文件和定义文件的开头。例:

/*

* Copyright (c) 2001,上海贝尔有限公司网络应用事业部

* All rights reserved.

*

* 文件名称: filename.h

* 文件标识: 见配置管理计划书

* 摘要: 简要描述本文件的内容

*

* 当前版本: 1.1

* 作者: 输入作者(或修改者)名字

* 完成日期: 2001年7月20日

*

* 取代版本:1.0

* 原作者: 输入原作者(或修改者)名字

* 完成日期: 2001年5月10日

*/

 

建议将成员函数的定义与声明分开,不论该函数体有多么小。

 

使用声明相当于输入密码时的确认。

 

1.5 目录结构

如果一个软件的头文件数目比较多(如超过十个),通常应将头文件和定义文件分别保存于不同的目录,以便于维护。例如可将头文件保存于include 目录,将定义文件保存于source 目录(可以是多级目录)。

 

2 章程序的版式

 

  【规则2-1-1】在每个类声明之后、每个函数定义结束之后都要加空行。

  【规则2-1-2】在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。

 

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

  【规则2-2-2ifforwhiledo 等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加{}。这样可以防止书写失误。

 【建议2-2-1】尽可能在定义变量的同时初始化该变量(就近原则)

 

2.3 代码行内的空格

ifforwhile 等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。

 

【规则2-3-4】‘,’之后要留空格,如Function(x,y, z)。如果‘;’不是一行的结束

符号,其后要留空格,如for(initialization; condition; update)。

 

if ((a>=b) &&(c<=d))   // 良好的风格。注意,这种情况之下<=两边就不用加空格了!!!

 

  【规则2-6-1】应当将修饰符* 和& 紧靠变量名

例如:

char *name; //name是一个指针,指向的类型是char

int *x, y; // 此处y 不会被误解为指针

 

public 类型的函数写在前面,而将private 类型的数据写在后面,采用这种版式的程序员主张类的设计“以行为为中心”,重点关注的是类应该提供什么样的接口(或服务)。

我建议读者采用“以行为为中心”的书写方式,即首先考虑类应该提供什么样的函

数。这是很多人的经验--“这样做不仅让自己在设计类时思路清晰,而且方便别人阅

读。因为用户最关心的是接口,谁愿意先看到一堆私有数据成员!”

 

3 章命名规则

 

【规则3-1-2】标识符的长度应当符合“min-length&& max-information”原则。

 【规则3-1-3】命名规则尽量与所采用的操作系统或开发工具的风格保持一致。

【规则3-1-6】变量的名字应当使用“名词”或者“形容词+名词”。

例如:

float value;

float oldValue;

float newValue;

  【规则3-1-7】全局函数的名字应当使用“动词”或者“动词+名词”(动宾词组)。类的成员函数应当只使用“动词”,被省略掉的名词就是对象本身。

例如:

DrawBox(); // 全局函数

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

【规则3-2-1】类名和函数名用大写字母开头的单词组合而成。

class LeafNode; // 类名

void SetValue(int value); // 函数名

规则3-2-2】变量和参数用小写字母开头的单词组合而成。

例如:

BOOL flag;

int drawMode;

  【规则3-2-7】为了防止某一软件库中的一些标识符和其它软件库中的冲突,可以为

各种标识符加上能反映软件性质的前缀。例如三维图形标准OpenGL 的所有库函数均以gl 开头,所有常量(或宏定义)均以GL 开头。如我们公司就有好多函数前面都加LIG

 

4 章表达式和基本语句

 

标准比较:

if (flag) // 表示flag 为真

 

if (!flag) // 表示flag 为假。

其他的方式都属于不良风格。

 

4.3.4 指针变量与零值比较

标准if 语句如下:

if (p == NULL) // p 与NULL 显式比较,强调p 是指针变量

if (p != NULL)

不要写成

if (p == 0) // 容易让人误解p 是整型变量

if (p != 0)

 

if (p) // 容易让人误解p 是布尔变量

if (!p)

 

5 章常量

  【规则5-3-1】需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义

文件的头部。为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。(得仔细考虑和权衡)

 

2001 Page 34 of 101

const float RADIUS = 100;

const float DIAMETER = RADIUS * 2;

5.4 类中的常量

 

怎样才能建立在整个类中都恒定的常量呢?别指望const 数据成员了,应该用类中

的枚举常量来实现。例如

class A

{

enum { SIZE1 = 1,  SIZE2 = 2}; // 枚举常量

int array1[SIZE1];

int array2[SIZE2];

};

 

枚举常量不会占用对象的存储空间。例

sizeof(A)= 12

 

注意,const会占用空间

class A

{

    const int m_i;

public:

    A(int i):m_i(i){}

};

例:

sizeof(A)= 4

 

 

6 章函数设计

  【规则6-1-1】参数的书写要完整,不要贪图省事只写参数的类型而省略参数名字。如果函数没有参数,则用void 填充。(同意,至少DWORDGetLastError(void);都是这样的)

例如:

float GetValue(void); // 良好的风格

float GetValue(); // 不良的风格

 

【规则6-1-2】参数命名要恰当,顺序要合理。

一般地,应将目的参数放在前面,源参数放在后面。

 

 

  【规则6-2-3】不要将正常值和错误标志混在一起返回。正常值用输出参数获得,而错误标志用return 语句返回。

  【建议6-2-1】有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,可以附加返回值。

例如字符串拷贝函数strcpy 的原型:

char *strcpy(char *strDest,const char *strSrc);

strcpy 函数将strSrc 拷贝至输出参数strDest 中,同时函数的返回值又是strDest

这样做并非多此一举,可以获得如下灵活性:

char str[20];

int length = strlen( strcpy(str, “Hello World”) );

 

【规则6-3-1】在函数体的“入口处”,对参数的有效性进行检查。

很多程序错误是由非法参数引起的,我们应该充分理解并正确使用“断言”(assert

来防止此类错误。

【规则6-5-1】使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的。

 

7 章内存管理

  【规则7-2-5】用free delete 释放了内存之后,立即将指针设置为NULL,防止产生“野指针”(可以采纳)

 

7.9 内存耗尽怎么办?

1)判断指针是否为NULL,如果是则马上用return语句终止本函数。

2)判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。

3)为new malloc 设置异常处理函数。

我的经验教训是:

1)越是怕指针,就越要使用指针。不会正确使用指针,肯定算不上是合格的程序员。

2)必须养成“使用调试器逐步跟踪程序”的习惯,只有这样才能发现问题的本质。

 

9 章  类的构造函数、析构函数与赋值函数

对于任意一个类A,如果不想编写上述函数,C++编译器将自动为A 产生四个缺省的函数,如

A(void); // 缺省的无参数构造函数

A(const A &a); // 缺省的拷贝构造函数

~A(void); // 缺省的析构函数

A & operate =(const A &a); // 缺省的赋值函数

纠正:以上观点不是太正确,这些函数只是在需要的时候才产生,看看汇编就知道了。

 

2)“缺省的拷贝构造函数”和“缺省的赋值函数”均采用“位拷贝”而非“值拷贝”的方式来实现,倘若类中含有指针变量,这两个函数注定将出错。

 

// 赋值函数

String & String::operate =(constString &other)

{

// (1) 检查自赋值

if(this == &other)

return *this;

// (2) 释放原有的内存资源

delete [] m_data;

// (3)分配新的内存资源,并复制内容

int length = strlen(other.m_data);

m_data = new char[length+1];

strcpy(m_data, other.m_data);

// (4)返回本对象的引用

return *this;

}

b = a;

c = b;

a = c;

 

1)第一步,检查自赋值。你可能会认为多此一举,难道有人会愚蠢到写出a = a 这

样的自赋值语句!的确不会。但是间接的自赋值仍有可能出现,例如

// 地址自赋值

b = &a;

a = *b;

也许有人会说:“即使出现自赋值,我也可以不理睬,大不了化点时间让对象复制自己而已,反正不会出错!”

 

他真的说错了。看看第二步的delete,自杀后还能复制自己吗?所以,如果发现自

赋值,应该马上终止函数。注意不要将检查自赋值的if 语句

if(this == &other)

错写成为

if( *this == other)

 

10 章类的继承与组合

 

注意,当前面向对象技术的应用热点是COM CORBA,这些内容超出了C++教材的范畴,请阅读COM CORBA 相关论著。

(感觉COM挺不好使的,找时间了解一下CORBA究竟是啥玩意)

 

11 章其它编程经验

【建议11-3-11】尽量使用标准库函数,不要“发明”已经存在的库函数。(同意)

【建议11-3-14】如果可能的话,使用PC-LintLogiScope 等工具进行代码审查。(目前没做起来,但我感觉我目前的BUG一般不在这些因粗心地方)

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值