C++11学习笔记之5—— 杂项一

  1. constexpr关键字

constexpr,从字面上看可以看出是用来声明常量表达式,但C++11不仅限于此。
constexpr同时可以声明常量函数和常量对象,这里的常量是指编译时期可以确定的常量。

普通的const关键字声明的函数在下面无效:

1
2
3
4
5
6
7
const int getArraySize() { return 32; }

int main()
{
int myArray[getArraySize()]; // Invalid
return 0;
}

使用constexpr重新定义:

1
2
3
4
5
6
7
constexpr int getArraySize() { return 32; }

int main()
{
int myArray[getArraySize()]; // OK
return 0;
}

也可以用常量构造函数来定义常量对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Rect
{
public:
constexpr Rect(int width , int height)
: mWidth(width), mHeight(height)
{} // 构造函数体必须为空

constexpr int getArea() const
{
// 必须只有一个return 语句,return 常量表达式;
return mWidth * mHeight; }
}
};

可以这样声明constexpr对象

1
2
3
constexpr Rect r(8,2);

int myArray[r.getArea()];
  1. 类型别名

C++11在typedef的基础上引入了一个新的类型别名机制,更容易理解。

1
using MyInt = int;

个人对这种方式也比较疑惑:将using 和 赋值符号放在一起,让人有些不知其所以然,不过从代码的角度来说还是比较好理解的。

同样可以用这个来声明函数指针类型,C++11之前的声明格式是

1
typedef ReturnType (*FuncPointerName)( ArgType );

例如:

1
typedef int (*FuncType)( char );

使用using声明格式是:

1
using FuncPointerName = ReturnType (*) (ArgType);

例如:

1
using FuncType = int (*) (ArgType);

这种方式相对于之前的MyInt声明更加直观。
对于成员函数指针来说可以像下面这样(多半用于模板类中的声明):

1
using ClassMempFunc = ReturnType (ClassType::*) (ArgType);
  1. 统一初始化
    对于结构体和类,我们通常可以用下面的方式进行初始化:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct BasicStruct
{
int x;
float y;
};

class BasicClass
{
public:
BasicClass( int x , int y )
: _X(x), _Y(y) { }

private:
int _X, _Y;
};

BasicStruct myStruct = { 10 , 2.5 };
BasicClass myClass( 10, 15);

C++11之前对于结构体可以调用{…}进行初始化,然而对于类,需要用(…)调用构造函数。
C++11允许用{…}进行对象初始化:

1
BasicClass myCalss = { 1015 }; // = 号也是可选的

这种统一初始化可以防止范围缩限(narrowing,又称窄化,我理解为数据截断),即当转换后的类型无法表示原类型时发出编译错误。

例如下面的函数调用:

1
2
3
4
void Func( int i ) { ... }

int x = { 3.14 }; // 去掉 { },编译错误会变成警告
Func( { 3.14 } ); // 去掉 { },编译错误会变成警告

对于类来说可以在某些场合代替构造函数:

1
2
3
4
BasicClass GetBasicClassObj( )
{
return { 10 , 15}; // 甚至不需要声明类名
}

同样也适用于STL容器的初始化:

1
std::vector<int> myVec = {10 , 20 , 30];

也可以用来初始化动态分配的数组:

1
int *pArray = new int [4] { 0, 1, 2, 3};

最后,也可以用于构造函数的成员初始化列表:

1
2
3
4
5
6
7
8
9
10
class MyClass
{
public:
MyClass ( )
: mArray{0, 1, 2, 3}
{}

private:
int mArray[4];
};

  1. 空指针

C++11之前,用0来代表空指针,可以这么写:

1
int* ptr = 0;

但是这常常会和整数0混淆:

1
2
3
void Func(char * pSrc) { ... } // 如果传入0作为空指针参数想调用这个函数,

void Func(int i ) { ... } // 而编译器实际上会调用整数版本的同名函数

因此C++11 引入了空指针专用的名称nullptr,很多以往的C++代码要改咯。

  1. 尖括号

在C++11之前,有个众所周知的尖括号问题无法解决: 模板参数类型的模板。

必须手动在两个尖括号之前添加空格:

1
vector<basic_string<wchart_t> >  vec; // >>可能被作为流输出运算符

C++11已经可以识别这类的声明:

1
vector<basic_string<wchart_t>>  vec; // OK in C++11