1 预处理器与编译器
预处理器在编译器之前运行,其根据程序员的指示,决定实际要编译的内容。预处理器指令都以#开头
1.1 使用#define定义常量
格式:#define 标识符 值
例如:
#define ARRAY_LENGTH 25
宏标识符一般使用大写字母
缺点:
- “死板的”文本替换
用#define定义常量时,预处理器只是进行死板的文本替换,而不检查替换是否正确,但编译器会检查。如下面例子:
#define FAV_WHISKY "Jack Daniels"
int main(){
string favoriteWhisky(FAV_WHISKY);
cout<<"My favorite drink is: "<<FAV_WHISKY<<endl;
return 0;
}
如果我们开始这样定义FAV_WHISKY
#define FAV_WHISKY 42
预处理器并不会觉得有问题,如果没有string favoriteWhisky(FAV_WHISKY);
这句,编译器也不会检查出问题,但输出的文本明显不合常理。
- 不能决定定义的常量的类型
如下面例子:
#define PI 3.1416
在预处理器看来,PI就是3.1416,而不知道其数据类型。在定义常量时,尽量使用const
const double PI =3.1416;
1.2 使用宏避免多次包含
为了避免类似递归包含的问题,可以使用宏及预处理指令#ifndef和#endif
1.3 使用#define编写宏函数
预处理器对宏指定的文本进行简单替换,因此可以使用宏来编写简单的函数。宏函数通常用于执行非常简单的计算,相比于常规函数,它的优点在于在编译前宏函数就展开,有些情况有助于改善代码性能(函数调用也有开销)
简单示例:
#define SQUARE(x) ((x)*(x))
#define MAX(a,b) (((a)>(b)?(a)):(b))
int main(){
int num=5;
cout<<"SQUARE("<<num<<") = "<<SQUARE(num)<<endl;
int num=7;
cout<<"MAX("<<num<<", "<<num2<<") = "<<;
cout<<MAX(num,num2)<<endl;
}
在编译时,上述代码被就地展开了,如:
cout<<(((num)>(num2)?(num)):(num2))<<endl;
1.3.1 宏函数的括号
由于宏的计算方式是预处理器支持的文本替换机制,所以在编写宏函数时我们使用大量的括号来避免计算时可能的歧义
如果将之前的代码改写为如下
#define SQUARE(x) x*x
用下面语句调用宏:
cout<<SQUARE(3+4);
展开后如下:
cout<<3+4*3+4;
将会先做乘法运算,结果将和我们预期的不一样
1.3.2 使用assert宏验证表达式
1.3.3 宏函数优点和缺点
优点:
- 减少代码行
- 简单宏的性能优于常规函数调用,函数调用需要额外开销
缺点:
- 宏不支持任何形式的类型安全
- 复杂宏不容易调试
2 模板
模板让程序员能够定义一种适用于不同类型对象的行为。这和宏有点类似,但宏不是类型安全的,模板是类型安全的
2.1 模板声明语法
模板声明以关键字template打头,接下来是类型参数列表,格式如下:
template <parameter list>
template function/class declaration..
关键字template标志模板声明开始,参数化列表包含关键字typename,它定义了模板参数objType,objType是一个占位符,针对对象实例化模板时,将使用对象的类型替换它。
2.2 模板函数
下面是一个模板函数的声明例子,它与MAX宏等价
template <typename T>
const T& GetMax(const T& value1,const T& value2){
if(value1>value2)
return value1;
else
return value2;
}
调用该模板的示例:
int num1=25;
int num2=40;
int maxVal=GetMax<int>(num1,num2);
调用GetMax时使用了
2.3 类型安全
当我们这样调用上一个例子时:
GetMax(num1,str1);
其中,str1是一个字符串类型变量。编译器将报错
2.4 模板类
例子:
template <typename T>
class HoldVarTypeT
{
private:
T value;
public:
void SetValue(const T& newValue){ value=newValue;}
T& GetValue(){return value;}
}
2.5 声明包含多个参数的模板
模板参数列表包含多个参数,参数之间用逗号分隔
2.6 声明包含默认参数的模板
2.7 模板的实例化和具体化
模板是创建类的蓝图,因此仅当模板类以某种方式被使用后,其代码才存在。对模板来说,实例化指的是使用一个或多个模板参数来创建特定的类型
关于模板的具体化可参考 https://www.cnblogs.com/cthon/p/9203234.html
2.8 模板类和静态成员
2.9 可变参数模板(C++11新增)
可变参数模板即一个可接受可变数目参数的模板函数或模板类。可变数目的参数被称为参数包。存在两种参数包:模板参数包,表示零个或多个模板参数;函数参数包,表示零个或多个函数参数。用一个省略号指出一个模板参数或函数参数表示一个包。具体例子见(p619)
template <typename T,typename ... Args>
void foo(const T &t,const Args& ... rest);
Args和rest分别表示两个名为Args和rest的模板参数包和函数参数包
2.9.1 sizeof...运算符
当我们需要知道包中有多少个元素时,可使用sizeof...运算符,其返回一个常量表达式,而且不会对实参求值:
template <typename ... Args> void g(Args ... args){
cout<<sizeof...(Args)<<endl;
cout<<sizeof...(args)<<endl;
}
通过支持参数数量可变的模板,C++打开了支持元组的大门。std::tuple就是实现元组的模板类