【笔记】C++的150个建议,第一章

读书笔记,书名:《编写高质量代码:改善C++程序的150个建议》
持续更新中...

目录
第一部分 语法篇

  1. 第一章 从C继承而来
  2. 第二章 C到C++
  3. 第三章 内存管理
  4. 第四章 类

第1章 从C继承而来

建议0:不要让main返回void

C++03中给出的2种main函数定义方式:

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

在linux中执行./out && echo "success",如果main函数return -1,则不会输出"success"
C++中还有个规定,如果main函数中不写return,编译器会隐式地加上return 0

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

  1. 整型0
  2. 空指针NULL,#define NULL 0。 C++中空指针被定义为nullptr
  3. 字符串结束符'\0'
  4. 逻辑false

指针与int类型所占空间一样,都是32位。

建议2:== 的正确使用方式

if (0 == value){
}

建议3:表达式的计算顺序

  1. 不确定优先级时,使用括号解决
  2. 函数的参数求值顺序不确定:printf("%d %d", fun_1(), fun_2() )
  3. 操作数的求职顺序不确定:a = p() + q() * r();
  4. 短路运算有顺序:a < b && c < d
  5. 三目运算有顺序:a < b? c: d

建议4:宏#define的陷阱

  1. 使用完备的括号
#define ADD(a, b) a + b      // 不完备,a+b*c+d
#define ADD(a, b) (a + b)    // 不完备,(a+b)*(c+d),碰巧没问题
#define MULTIPLY(a, b) (a * b)    // 不完备,MULTIPLY(a+b, c) -> (a+b*c)
#define ADD(a, b) (a) + (b)  // 不完备
#define ADD(a, b) ((a) + (b))  // 完备
#define MULTIPLY(a, b) ((a) * (b))  // 完备
  1. 调用宏定义函数时,不要使参数发生变化
#define SQUARE( a ) ((a) * (a)) 
int Square(int a) { return a*a; } 

int nValue1 = 10, nValue2 = 10; 
int nSquare1 = SQUARE(nValue1++);   // nSquare1=110, nValue1=12 
int nSquare2 = Square(nValue2++);   // nSquare2=100, nValue2=11
  1. 用大括号将多条表达式括起来

建议5:指针必须赋初始化值

如果不赋初值,编译器会初始化一个随机值,这将是很危险的。

int* p = NULL;
int* q = new int(1);

建议6:逗号分隔符

表达式1, 表达式2, 表达式3, ..., 表达式n

整个逗号分隔表达式的值为表达式n的值。

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

在处理字符串、数组时,时刻提防内存溢出。

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

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

为了避免同一个文件被包含多次,C/C++中有两种处理方式,一种是#ifndef方式,另一种是#pragma once方式。
注意:不同的头文件不能使用相同的宏名,否则编译器会报"找不到声明"的错误

  1. 方式1:
#ifndef __<PROJECT>_<PATH>_<FILE>_H__
#define __<PROJECT>_<PATH>_<FILE>_H__
// 声明、定义语句 
#endif 
  1. 方式2:(GCC已经取消支持,Visual Studio还支持)
#pragma once 
// 声明、定义语句

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

字节对齐:为了提高变量的访问效率,某些特定类型的数据只能从某些特定地址存取,以空间换时间。

struct A{
  int a;
  char b;
  char reserved;  // 保留字节,空间换时间
  short c;
};

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

C++的四种强制转换形式:

  1. const_cast<T*> (a),从类中去除一下属性:const、volatile、__unaligned。不推荐使用。
  2. dynamic_cast<T*> (a),"类的安全的向下转换",用于将基类指针转换为派生类指针,基类指针可以指向派生类对象,能实现多态,转换是安全的。
  3. reinterpret_cast<T*> (a),"重新解释",不相关类型之间的转换,是不安全的。
  4. static_cast<T*> (a),相当于传统的C语言里的强制转换,在运行时转换过程中,不进行类型检查,是不安全的。
class Base {
};

class Derived : public Base {
};

int main() {

    Base *pB = new Base();
    if (Derived *pD = static_cast<Derived *>(pB)) {
        // 编译期和运行时不做类型检查
        // 向上转换是不安全的(坚决抵制这种方法)
    }

    Derived *pD = new Derived();
    if (Base *p1 = static_cast<Base *>(pD)) {
        // 向下转换是安全的
    }

    if (Derived *p2 = dynamic_cast<Derived *>(pB)) {
        // 编译期报错,error: 'Base' is not polymorphic
    }
    if (Base *p3 = dynamic_cast<Derived *>(pD)) {
        // 向下转换是安全的
    }
    return 0;
}

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

后缀操作过程中会构造一个临时对象,因此效率不如前缀操作。
但是,对于整型和长整型而言,性能区别通常可以忽略。
另外,编译器一般会对后缀操作符进行优化。

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

  1. 尽量不在循环内定义变量或实例化对象
  2. 定义变量尽可能晚、尽可能local

建议14:小心typedef的陷阱

  1. typedef与#define的区别

define只是简单的字符串替换,typedef具有一定的封装性,更易于定义变量。

typedef int* PTR_INT1
#define PTRINT2
PTR_INT1 pNum1, pNum2;  // pNum2是int*类型
PTR_INT2 pNum3, pNum4;  // nNum4是int类型
  1. typedef可以定义平台无关的类型,如uint64_t
#if __WORDSIZE == 64
typedef unsigned long int   uint64_t;
#else
__extension__
typedef unsigned long long int  uint64_t;
#endif
  1. typedef是一个存储类的关键字,类似于static、mutable、register等,因此不能与这些关键字一起使用。

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

1.可变参数的函数没有类型检查,运行时会报错:

printf("%d %d", "Hello", 2019);

2.禁用了语言类型检查,容易出错,不安全。
3.不支持自定义数据类型。

可以尝试Boost中的format替换printf。

建议16:禁用goto

建议17:小心隐式转换

  1. 基本类型之间的隐式转换,要注意精度损失问题。
  2. C++中规定T*void*单向的转换,是安全的。而C中允许双向转换,不安全。
  3. 不明确的构造函数,不检查参数类型;转换过程中会调用类的构造函数、析构函数。
//编译器会隐式地将 float 类型的 fVal 转成 int ,然后调用类A的构造函数。
//解决方法:为单参数的构造函数加上explicite关键字,防止隐式转换。
class A
{
public:
    A(int x): m_data(x){}
private:
    int m_data;
}
void DoSomething(A a);
float fVal = 1.23f;
DoSomething(fVal);
  1. 安全的隐式转换:子类到基类的转换、const到non-const的转换

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

void是『无类型』,void*是『无类型指针』。void*可以指向任何类型。
如果函数的参数可以是任意类型指针,应声明为void*类型:

void* memcpy(void* dest, const void* src, size_t len);
void* memset(void* buffer, int c, size_t num);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容