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时使用了,这将模板参数T指定为了int。实际上,在调用模板函数时不一定要指定类型,但对于模板类,须显示指定。

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就是实现元组的模板类

2.10 使用static_assert执行编译阶段检查

posted on 2024-05-08 13:30  房东的猫hhhh  阅读(16)  评论(0编辑  收藏  举报