读书笔记 effective c++ Item 2 尽量使用const,枚举(enums),内联(inlines),不要使用宏定义(define)
这个条目叫做,尽量使用编译器而不要使用预处理器更好。#define并没有当作语言本身的一部分。
例如下面的例子:
#define ASPECT_RATIO 1.653
符号名称永远不会被编译器看到。它可能在源码到达编译器之前被预处理器移除。ASPECT_RATIO 最终不会进入符号表,如果因为这个常量的使用而导致编译错误,会使你非常迷惑,因为错误信息会指向1.653而不是ASPECT_RATIO。如果ASPECT_RATIO被定义在一个不是你自己写的头文件中,你会不知道1.,653来自哪里。,
解决方法是将宏替换成常量:
Const double AspectRatio = 1.653
作为一个语言常量,AspectRatio能够被编译器看到,编译器也肯定能进入到AspectRatio的符号表中。此外,对于浮点常量来说,使用常量比使用宏定义会产生更少的代码。因为预处理器会盲目的将宏定义名称ASPECT_RATIO替换成1.653,这会造成在目标码中1.653的多份拷贝,而常量的使用最多产生一份拷贝。
当用常量替换宏定义的时候,有两种特殊情况值得提一下:
第一种是定义常量指针,因为常量定义会被放到头文件中,很多文件会包含这个头文件,将指针声明成常量,同时将指针指向的内容也声明成常量。为了在头文件中定义一个基于char*的字符串,必须写const两次:
Const char* const authorname = “Scott”
在这里有必要提醒一下使用string对象要优于基于char*的字符串,所以将authorname定义成如下方式更好:
const std::string authorname(“Scott”);
第二种特殊的情况是关于类中指定的常量。为了将常量的作用域限制在类中,必须将其声明成一个成员,为了保证至多只有一份常量的拷贝,你必须将其声明成static 成员:
Class GamePlayer{
Private:
Static const int NumTurns = 5;
Int scores[NumTurns];
}
上面看到的是NumTurns的声明而非定义,c++需要你为你所使用的任何东西(anything)提供一份定义,但是类专属的静态整型常量(intergers,chars,bools)是一个例外,只要你不使用他们的地址,你可以声明并且使用他们而不用提供一个定义。如果你需要取得类专属常量的地址或者你所使用的编译器错误的坚持类专属常量需要一个定义(即使不需要获取地址),你需要提供一个单独的定义:
Const int GamePlayer::NumTurns;
你需要把定义放到实现文件而不是头文件中。因为类专属对象的初始值是在声明时提供的,不允许在定义的时候对其进行初始化。
顺便说一句,不可以使用宏定义为类定义专属常量,因为宏定义没有作用域。一旦一个宏定义被定义,它就在余下的编译中有效(只要它没有被undefed)。这意味者宏定义不能当作类专属常量,它们也不能用来提供任何类型的封装,例如,没有私有的#define.
旧的编译器也许不会接受上面的语法,因为在过去,为静态类成员在声明处提供初始值是非法的,此外,只允许整型和常量进行类内部的初始化。一旦上面的语法不能用了,你需要把初始化值放在定义处。
Class costEstimate{
Private:
Static const double FudgeFactor;
}
Const double costEstimate::FudgeFactor=1.35;
这是你任何时候需要做的,唯一的例外是在类编译过程中你需要一个类常量值,例如在类中声明一个数组,在编译过程中需要知道数组的大小。这时候在类内部为静态整型常量值指定初始值是被禁止的(这是不正确的),补偿的做法是使用”enum hack”.这种技术利用了一个事实:枚举类型的值可被用在需要整型值的地方,所以可以如下定义:
Class Gameplayer
{
Private:
Enum{NumTurns=5};
Int scores[NumTurns];
}
Enum hack技术值得被了解,有以下几个原因:
第一, enum hack的行为在一些情况下更像宏定义而不是const,有时候这也是你所需要的。例如:取得const的地址是合法的,但获取枚举的地址是不合法的,同样的,获取宏定义的地址是不合法的。如果你不想让其他人获取指向整型常量的指针或者引用,枚举是进行这种约束的一个好的方法。同样,虽然好的编译器不会为整型常量分配额外的空间,一个草率的编译器可能会这么做,这是你不愿意看到的。像宏定义一样,枚举永远不会产生这样的不必要的内存分配。
第二, enum hack是很实用的技术,很多代码都会使用到它,因此当你看到它你应给能够识别出来。事实上,enum hack是模板元编程的基本技术。
回到预处理器,#define的另外一个用法是实现一个看上去像函数的宏,但对其调用不会招致额外开销。下面是一个取最大值的例子:
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))
这样的宏有许多缺点,想想都头疼。
当你实现这类宏时,你必须记住对宏定义体中的所有参数都要加上括号,否则别人在表达式中调用宏的时候会遇到 麻烦。但是即使你那么做了,你仍然会遇到奇怪的事情
int a = 5, b = 0;
CALL_WITH_MAX(++a, b); // a is incremented twice
CALL_WITH_MAX(++a, b+10); // a is incremented once
这里a加一的次数取决于a和一个多大的数进行比较。
幸运的是,你可以不必忍受这么无聊的事情。你可以通过定义一个内联函数模板来获得宏定义函数所有的效率并且可预知函数的所有行为,函数也是类型安全的。
template<typename T> // because we don’t
inline void callWithMax(const T& a, const T& b) // know what T is, we
{
F(a>b?a:b);
}
这个宏定义会产生一个函数族,每个函数将相同类型两个对象作为参数,其中较大的调用f,不必给函数体内部的参数加括号,也不必担心参数会被求值多次。并且因为callWithMax是一个函数,它遵循作用域和访问规则。比如,你可以写出一个类的私有内联函数。宏定义却不能够做到。
鉴于consts,enums和inlines的实用性,你可以减少预处理器的使用,但是它并没有被清除,#inlcude仍然是必要的,#ifdef和#ifndef在编译控制上仍然发挥重要作用,还没有到让预处理器退休的时候,但是你绝对可以给它放一个长长的假期。
读书笔记 effective c++ Item 2 尽量使用const,枚举(enums),内联(inlines),不要使用宏定义(define)的更多相关文章
- 读书笔记 effective c++ Item 26 尽量推迟变量的定义
1. 定义变量会引发构造和析构开销 每当你定义一种类型的变量时:当控制流到达变量的定义点时,你引入了调用构造函数的开销,当离开变量的作用域之后,你引入了调用析构函数的开销.对未使用到的变量同样会产生开 ...
- 读书笔记 effective c++ Item 27 尽量少使用转型(casting)
C++设计的规则是用来保证使类型相关的错误不再可能出现.理论上来说,如果你的程序能够很干净的通过编译,它就不会尝试在任何对象上执行任何不安全或无意义的操作.这个保证很有价值,不要轻易放弃它. 不幸的是 ...
- 读书笔记 effective c++ Item 22 将数据成员声明成private
我们首先看一下为什么数据成员不应该是public的,然后我们将会看到应用在public数据成员上的论证同样适用于protected成员.最后够得出结论:数据成员应该是private的. 1. 为什么数 ...
- 读书笔记 effective c++ Item 30 理解内联的里里外外 (大师入场啦)
最近北京房价蹭蹭猛涨,买了房子的人心花怒放,没买的人心惊肉跳,咬牙切齿,楼主作为北漂无房一族,着实又亚历山大了一把,这些天晚上睡觉总是很难入睡,即使入睡,也是浮梦连篇,即使亚历山大,对C++的热情和追 ...
- 读书笔记 effective c++ Item 38 通过组合(composition)为 “has-a”或者“is-implemented-in-terms-of”建模
1. 什么是组合(composition)? 组合(composition)是一种类型之间的关系,这种关系当一种类型的对象包含另外一种类型的对象时就会产生.举个例子: class Address { ...
- 读书笔记 effective c++ Item 4 确保对象被使用前进行初始化
C++在对象的初始化上是变化无常的,例如看下面的例子: int x; 在一些上下文中,x保证会被初始化成0,在其他一些情况下却不能够保证.看下面的例子: class Point { int x,y; ...
- 【Effective C++ 读书笔记】条款02: 尽量以 const, enum, inline 替换 #define
条款02: 尽量以 const, enum, inline 替换 #define 这个条款或许可以改为“宁可以编译器替换预处理器”. 编译过程: .c文件--预处理-->.i文件--编译--&g ...
- 读书笔记 effective c++ Item 24 如果函数的所有参数都需要类型转换,将其声明成非成员函数
1. 将需要隐式类型转换的函数声明为成员函数会出现问题 使类支持隐式转换是一个坏的想法.当然也有例外的情况,最常见的一个例子就是数值类型.举个例子,如果你设计一个表示有理数的类,允许从整型到有理数的隐 ...
- 读书笔记 effective c++ Item 54 让你自己熟悉包括TR1在内的标准库
1. C++0x的历史渊源 C++标准——也就是定义语言的文档和程序库——在1998被批准.在2003年,一个小的“修复bug”版本被发布.然而标准委员会仍然在继续他们的工作,一个“2.0版本”的C+ ...
随机推荐
- iOS开发——打包静态库与Framework
最近老是需要做接口给别的客户,就顺便把打包的过程也写一下吧! 一.静态库 静态图里面只能是纯文件,里面不能再有第三方打包的静态库,也就是说,静态库不能打包静态库.这个用的比较多,一般自己公司写出来的东 ...
- mysql迁移-----拷贝mysql目录/load data/mysqldump/into outfile
摘要:本文简单介绍了mysql的三种备份,并解答了有一些实际备份中会遇到的问题.备份恢复有三种(除了用从库做备份之外), 直接拷贝文件,load data 和 mysqldump命令.少量数据使用my ...
- STM32标准IIC驱动
IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接 微控制器及其外围设备.也是目前很流行的通讯总线,使用IIC总线做产品能够很大 ...
- 使用flexbox来布局web应用
使用 flexbox 可以帮助你设计出引人注目的布局,并且在pc端或移动端能够很好的缩放.告别使用浮动的 <div> 元素.绝对定位 和一些JavaScript hacks, 使用仅仅几行 ...
- AFNetworing进行POST上传 分类: ios技术 2015-04-01 17:03 73人阅读 评论(0) 收藏
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; //申明返回的结果是json类型 m ...
- UVa 136 - Ugly Numbers
题目大意:只有素因子2,3,5的数叫做丑数.输出第1500个丑数即可. 这个...好吧,直接输出就是了.自己写一个小程序先计算一下,这就是黑盒测试的好处啊,“我们的目标是解决问题,而不是为了写程序而写 ...
- python pandas dataframe to_sql方法error及其解决
今天遇到了一个问题,很是奇怪,自己也想了一个另类的方法将其解决了,现在将详细过程经过记录如下: 我在处理完一个dataframe之后,需要将其写回到数据库.这个dataframe比较大,共有53列,7 ...
- HTML5学习笔记三:aside元素,time元素与微格式
一.aside元素 表示当前页面或文章的附属信息部分,相关的引用,侧边栏,广告等有别于主要内容的部分:主要有一下两种用法: 1. 被包含在article元素中作为主要内容的附属信息部分,可以是与当前文 ...
- vdi、vhd、vmdk虚拟格式转换
VirtualBox带来VBoxManager.exe,可以来转换格式. 命令如下(Windows环境,Linux版的应该也有VBoxManager这个二进制文件): VBoxManager存在于Vi ...
- JDBC oracle 错误总结
ORA-28040: No matching authentication protocol jdk:1.8 oracle:12c 使用ojdbc14.jar 报错:ORA-28040: No mat ...