《编写高质量代码改善C++程序的150个建议》读书笔记

《编写高质量代码改善C++程序的150个建议》读书笔记

  • 第一部分 语法篇
    • 第1章 从C继承而来的
      • 建议0:不要让main函数返回void
      • 建议1:区分0的四种面孔
      • 建议2:避免那些有运算符引发的混乱
      • 建议3:对表达式计算顺序不要想当然
      • 建议4:小心宏#define使用中的陷阱
      • 建议5:不要忘记指针变量地初始化
      • 建议6:明细逗号分隔表达式的奇怪之处
      • 建议7:时刻提防内存溢出
      • 建议8:拒绝晦涩难懂的函数指针
      • 建议9:防止重复包含头文件
      • 建议10:优化结构体中元素的布局
      • 建议11:将强制转型减到最少
      • 建议12:优先使用前缀操作符
      • 建议13:掌握变量定义的位置与时机
      • 建议14:小心typedef使用中的陷阱
      • 建议15:尽量不要使用可变参数
      • 建议16:慎用goto
      • 建议17:提防隐式转换带来的麻烦
      • 建议18:正确区分void与void*
    • 第2章 从C到C++,需要做出一些改变
      • 建议19:明白再C++中如何使用C
      • 建议20:是应用memcpy()系列函数时要足够小心
      • 建议21:尽量用new/delete代替malloc/free
      • 建议22:灵活地使用不同风格的注释
      • 建议23:尽量使用C++标准的iostream
      • 建议24:尽量采用C++风格的强制转型
      • 建议25:尽量用const、enum、inline替换#define(尽量把工作交给编译器而非预处理器)

第一部分 语法篇

第1章 从C继承而来的

建议0:不要让main函数返回void

main函数只有以下两种定义方法是正确的:

int main(void)
int main(int argc, char *argv[])

main返回值的作用:

int main()
{
    return 0;
}

上面的代码在Linux环境下,采用命令:

g++ main.cpp

生成可执行文件a.out,然后,执行命令:

./a.out && ehco "success"

结果输出success
如果上述程序为:

int main()
{
    return -1;
}

则无输出。

建议1:区分0的四种面孔

  • 整形0
    00000000 00000000 00000000 00000000
  • 空指针NULL
    32位,代表地址
  • 字符串结束标志 ‘\0’
    占8位,00000000
  • 逻辑FALSE/false
    FALSE/TRUE 是int类型,占32位,false/true 是bool类型,占1位

建议2:避免那些有运算符引发的混乱

注意 == 和 = 的区别,为了防止出现问题,可以通过良好的代码习惯避免。

if(0 == n)
{
	// do something
}

此外还有&&和&,||和|的区别。

建议3:对表达式计算顺序不要想当然

熟悉操作符的优先级,多写几个括号。
如:

if(n & m == k)

if((n & m) == k)

注意函数参数和操作数的评估求值顺序问题。
如:

int i = 2010;
printf("the result are: %d %d", i, i+=1);

printf("the result are: %d %d", i, i+1);
a = p() + q() * r();

int para1 = p();
int para2 = q();
a = para1 + para2 * r();

建议4:小心宏#define使用中的陷阱

(1)使用宏定义表达式时,要使用完备的括号
如:

#define ADD(a, b) a + b
#define ADD(a, b) (a + b)
#define ADD(a, b) (a) + (b)

如果计算ADD(a, b) * ADD(c, d),本意是对(a+b)*(c+d)求值,但代码展开后变成了如下形式:

a + b * c + d
(a + b) * (c + d)
(a) + (b) * (c) + (d)

又如:

#define MULTIPLE(a, b) (a * b)

在计算(a+b)*c时,得到

(a + b * c)

要避免这些问题,要做的就是:用完备的括号完备地保护各个宏参数。正确的定义应为:

#define ADD(a, b) ((a) + (b))
#define MULTIPLE(a, b) ((a) * (b))

(2)使用宏时,不允许参数发生变化,警惕++,–等运算符。
《编写高质量代码改善C++程序的150个建议》读书笔记_第1张图片
(3)用大括号将宏定义地多条表达式括起来。
《编写高质量代码改善C++程序的150个建议》读书笔记_第2张图片

建议5:不要忘记指针变量地初始化

在适应局部指针变量时,一定要将其初始化。对于全局变量来说,在声明地同时,编译器会完成对变量地初始化。

建议6:明细逗号分隔表达式的奇怪之处

例如:

if(++x, --y, x<20 && y>0)

if的判断以最后一个表达式为准。

建议7:时刻提防内存溢出

在调用C语言字符串经典函数(如strcpy,strcat,gets等)时,尽量追踪传入数据的流向。在访问数据时,注意对边界数据要特殊情况特殊处理。还要杜绝使用未初始化指针和失效后未置NULL的“野指针”。

建议8:拒绝晦涩难懂的函数指针

如:

void (*p[10])(void(*)());

改为

typedef void(*pfv)();
typedef void (*pFun_taking_pfv) (pfv);
pFun_taking_pfv p[10];

使用typedef可以让函数指针更直观和易维护。

建议9:防止重复包含头文件

// 解决方法1
#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__
// statements
#endif

//解决方法2,仅VC++支持
#pragma once

建议10:优化结构体中元素的布局

如下面的代码:

struct A
{
    int a;
    char b;
    short c;
};   //sizeof(struct A) = 8
struct B
{
    char b;
    int a;
    short c;
};   //sizeof(struct B) = 12

了解结构体中元素的对齐规则,合理地为结构体元素进行布局。这样不仅可以有效地节约空间,还可以提高元素的存取效率。

建议11:将强制转型减到最少

// 将表达式的类型转换为T
(T) expression
T(expression)

这两种形式之间没有本质上的区别。
新风格的强制转型:

  • const_cast(a)
    它用于从一个类中去除以下这些属性:const、volatile和__unaligned.
class A { //...};
void Function()
{
    const A *pConstObj = new A;
    A *pObj = pConstObj; //ERROR: 不能讲const对象指针赋值给非const 对象
    pObj = const_cast<A*>(pConstObj); //OK
}
  • dynamic_cast(a)
    它将a值转换成类型为T的对象指针,主要用老实现类层次结构的提升。
class B { //...};
class D: public B { //...};
void Function(D *pObjD)
{
    D *pObj = dynamic_cast<D*>(pObjD)
}

建议12:优先使用前缀操作符

浅醉操作符省去了临时对象的构造,因此它在效率上由于后缀操作。

建议13:掌握变量定义的位置与时机

建议14:小心typedef使用中的陷阱

用途:

  1. 声明struct对象
  2. 定义一些与平台无关的类型
  3. 为复杂的声明定义一个简单的别名

建议15:尽量不要使用可变参数

可变参数的类型和个数在函数中由程序代码控制,导致编译器对可变参数的函数原型检查不够严格,难于查错,不利于写出高质量的代码。

建议16:慎用goto

建议17:提防隐式转换带来的麻烦

  1. 基本类型之间隐式转换(int,float)
  2. T* 指针到void* 的隐式转换
  3. 使用具名转换函数(cout<<1/2<
  4. 使用explicit限制的构造函数

建议18:正确区分void与void*

void 无类型
void 无类型指针

第2章 从C到C++,需要做出一些改变

建议19:明白再C++中如何使用C

extern "C"

建议20:是应用memcpy()系列函数时要足够小心

对于POD对象,可以通过对象的及地址和数据成员的偏移量获得数据成员的地址,但是在C++中,非POD对象的内存布局不确定。

建议21:尽量用new/delete代替malloc/free

new/delete在管理内存的同时调用了构造和析构函数,而malloc/free仅仅实现了内存分配与释放。
malloc/free时C/C++语言的标准库函数,而new/delete是C++的运算符。

建议22:灵活地使用不同风格的注释

// 以及/* */

建议23:尽量使用C++标准的iostream

建议24:尽量采用C++风格的强制转型

参考建议11

建议25:尽量用const、enum、inline替换#define(尽量把工作交给编译器而非预处理器)

在预处理阶段,预处理器会完成宏替换,因为此过程并不在百衲衣过程中进行,所以难以发现潜在的错误以及其他代码维护问题。

未完待续

你可能感兴趣的:(读书笔记,C++)