让自己习惯C++
条款 01:视C++为一个语言联邦
View C++ as a federation of languages
如今的C++是个多重范型编程语言(multiparadigm programming language),它支持如下形式:
- procedual(过程形式)
- object-oriented(面向对象形式)
- functional (函数形式)
- generic(泛型形式)
- metaprogramming(元编程形式)
最简单的办法是把它看作一个由相关次语言组成的联邦:
- C:即C语言,其实C++不过是较高级的C解法,它主要有:
- blocks(区块)
- statements(语句)
- preprocessor(预处理器)
- built-in data types(内置类型)
- arrays(数组)
- pointers(指针)
- Object-Oriented C++:面向对象设计
- classes(构造与析构函数)
- encapsulation(封装)
- inheritance(继承)
- polymorphism(多态)
- dynamic binding(动态绑定—即virtual函数)
- Template C++:这是C++泛型编程(generic programming)的部分,templates带来了TMP(templates metaprogramming—即模板元编程)
- STL:本质就是个template程序库,它对容器、迭代器、算法以及函数对象的规约有极佳的紧密配合与协调
条款 02:尽量以const,enum,inline替换#define
Prefer consts,enums,and inlines to #defines
宁肯以编译器替换预处理器!因为#define不被看作语言的一部分
当你做出如下定义:
#define ASPECT_RATIO 1.653
- 1
它在编译器开始处理源码之前就被预处理器移走,编译器不会看到它,所以它也不存在于记号表(symbol table)中。当你使用这个常量报错时会发现,错误信息也许会提到1.653而非ASPECT_RATIO,你将因为追踪它而浪费时间。
解决办法就是以常量替换宏定义:
const double AspectRatio = 1.653
- 1
它的另外一个优点是缩短代码长度,因为预处理器会盲目的将ASPECT_RATIO替换为1.653导致出现多份1.653。
两种特殊情况
- 常量指针:要想在头文件定义一个常量字符串必须写两次const
- const char* const authorName = "Scout Meyers"; //char*也可用string替换
- class专属常量:如果想将一个常量作用域限制在一个类中,你必须让这个常量成为类的一个 member(成员),而且为了让这个类内常量至多只有一份实体,还必须让它成一个 static成员
- class GamePlayer {
- private:
- static const int NumTurns = 5; //常量声明式
- int scores[NumTurns]; //使用该常量
- ......
- };
声明和定义:声明和定义是完全同的概念,声明是告诉编译器“这个函数或者变量可以在哪找到,它的模样像什么”。而定义则是告诉编译器,“在这里建立变量或函数”,并且为它们分配内存空间。
一般C++要求你给你使用的任何东西提供一个定义式,除非它是class专属常量&&static类型&&整数类型(ints、chars、bools),只要不取地址就无须写定义式。
非要看定义式的话、给你
const int GamePlayer::NumTurns;
- 1
需要注意!这个式子要放在实现文件而非头文件,原因往上瞅瞅。
其实这就是“in-class”初值设定(在编译期间需要一个class常量值,只针对整数常量):声明时设定初值,定义时不再设。当然如果不是整数类型的话也可以定义时再设初值。
enum hack补偿法
如果恰巧编译器秀逗了,你还可以使用枚举法设定初值:
class GamePlayer{
private:
enum { NumTurns = 5 };
int scores[NumTurns];
......
};
- 1
- 2
- 3
- 4
- 5
- 6
enum还有以下优点:
- enum hack 行为比较像 #define 而非 const ,取const地址是合法的,而取enum和#define地址通常是不合法的,enum 和 #define 一样绝不会导致非必要的内存浪费
- 它是(TMP)模板元编程的基础
inline
一个常见的#define误用情况是去实现宏(macros),宏看起来像函数,但它不会招致函数调用带来的额外开销。
来看一个简单的宏:
// 以a和b的较大值调用f
#define CALL_WITH_MAX(a,b) f( (a) > (b) ? (a) : (b) )
- 1
- 2
宏有两个特点
- 丑
- 无论何时,都要记得给宏中的所有参数加上小括号
用inline函数代替
template<typename T>
inline void callWithMax( const T& a, const T& b){
f( a > b ? a : b );
}
- 1
- 2
- 3
- 4
此外由于它是一个真正的函数,它遵守scope(作用域)的概念和访问规则,因此,完全可以封装在类内,这是宏无法做到的。