Effective C++ 01 C++和#define 木灵的炼金工作室

这是Effective C++的学习记录。

条款01:视C++为一个语言联邦

C++语言规范集成了大量的编程理念,这就导致了C++编程没有固定的范式,它可以是过程的、面向对象的、模块化的、泛型的或者元编程形式的,它的应用灵活性非常高,编写一个C++项目主要来讲存在这四个角度:

  • 过程主导的C语言式编程:C++的底层实现大部分依赖于C,我们就可以用C++来写C
  • 面向对象:是C with Classes的理念,包括数据封装原则等的一系列面向对象编程规范被引入
  • 泛型:比较小众的次语言
  • STL:拥有固定接口定义的程序库

根据需要实现的功能不同,我们的编程核心思维理念可能会在上述四个中不断切换,你甚至可以在同一份文件中体现不同的编程方略。
故不可以将C++认为是一个具有唯一守则的语言,理解这一点是写好C++的基础。

条款02:以const, enum, inline替换#define

C++的宏在编译时是由预处理器预先处理的,如果使用如下的写法,

#define pi 3.14
....
    S = r * r * pi;

编译器的其他部分看到的语句将是

    S = r * r * 3.14

对应的非终结符号将是id** = **id** * **id** * **num,而num类型的token是不会进入记号表的,这就会导致几个问题

  • 如果发生编译错误,由于编译器的较后部分无法看到宏变量的名称pi,编译器返回的信息是字面的数值,如3.14。如果是3.14还好,但如果这个宏的值是一个其它的数,而这个宏所在的头文件又刚好是其他人写的呢?
  • 由于编译器认为宏变量是一个数字型词法单元,故词法分析器不会将它加入符号表中,这就会导致我们编译的中间码中出现大量的数字字面量,这减慢了编译速度和编译器后端优化效率
  • 同时#define不重视作用域,这可能不利于数据封装

同时,使用宏来实现简单的函数会出现更严重的问题:如

#define max(a, b) (a) > (b) ? (a) : (b) //务必记得小括号
....
int a = 15;
max(++a, 10);  // 此时a被累加2次
max(++a, 20);  // 此时a被累加1次

我们发现,由于宏的实现是直接使用宏表达式替换原表达式,没有传参压栈的过程,自增运算就可能出现问题(较大的表达式会在?后的语句中被多调用一次)。这个max实现无法得到稳定的结果,所以我们只能使用inline函数。


Copyright AmachiInori 2017-2021. All Right Reserved.
Powered By Jekyll.
amachi.com.cn