说明
1. 新增关键词nullptr。
新增的nullptr是用来替代0或者NULL,来表示指向空(no value)的指针。
下面是一个例子:
void func(int i) {
cout << "func(int i)" << endl;
}
void func(void *p) {
cout << "func(void *p)" << endl;
}
int main() {
int i = NULL;
int j = 0;
std::nullptr_t p = nullptr;
func(i);
func(j);
func(p);
}
运行的结果:
func(int i)
func(int i)
func(void *p)
nullptr的类型是std::nullptr_t,定义于<cstddef>。
2. 增加auto来判断变量或对象类型。
auto可以用在各种类型上,包括lambda上也可以用。
但是auto必须要初始化操作,因为它需要初始化值来推导。
3. 使用{}引入“一致性初始化",或者也称为“列表初始化”。
列表初始化例子:
int main() {
int i{};
cout << "i = " << i << endl;//会有初始化,打印0
int j = {};
cout << "j = " << j << endl;//会有初始化,打印0
//int k = { 2.0 }; //报错,因为有narrowing
vector<int> v1 = { 1, 2, 3 };
vector<int> v2{ 4, 5, 6 };
for (auto a = v1.begin(); a != v1.end(); a++) {
cout << *a << endl;
}
for (auto a = v2.begin(); a != v2.end(); a++) {
cout << *a << endl;
}
}
以上是基本类型的列表初始化(vector应该不算)。
但是有一个问题,i{}和j = {}是不是有什么差别?
对于基本类型,应该没有差别吧,因为本来也不涉及到构造函数。对于类的话,应该有通用的指明初始值列的构造函数。这需要使用到class template std::initializer_list<>。
下面是类的列表初始化示例:
class CLS {
private:
int age;
int height;
public:
CLS(int a, int h) : age(a), height(h){
cout << "CLS(int a, int h)" << endl;
}
CLS(std::initializer_list<int> vals) {
cout << "CLS(std::initializer_list<int> vals)" << endl;
auto a = vals.begin();
age = *a;
++a;
height = *a;
}
void print(void) {
cout << "age : " << age << endl;
cout << "height : " << height << endl;
}
};
int main() {
CLS c1(1, 2);
c1.print();
CLS c2{ 3, 4 };
c2.print();
CLS c3 = { 5, 6 }; //调用的构造函数同c2{3, 4}
c3.print();
return 0;
}
运行的结果:
CLS(int a, int h)
age : 1
height : 2
CLS(std::initializer_list<int> vals)
age : 3
height : 4
CLS(std::initializer_list<int> vals)
age : 5
height : 6
4. c++11引入一种崭新的for循环形式,其实就是foreach循环。
下面是例子:
int main() {
for (int a : {1, 2, 3, 4}) {
cout << a << endl;
}
vector<int> v{ 5, 6, 7, 8 };
for (auto& a : v) {//使用引用,就可以修改至,否则就不能修改;
a *= 2;
}
for (auto a : v) {
cout << a << endl;
}
}
5. std::move,右值引用。
这个比较麻烦,这里不细讲了。
6. 关键字noexcept。
noexcept指明了函数不会抛出异常,或者它可能也或抛出异常,但是我们也处理不了,就不用管异常处理的事情了。
需要说明编译的时候是不会去管noexcept函数里面有没有抛出异常的,只是在运行时,如果一个noexcept函数抛出了异常,程序就会调用ternimate来终止程序本身。
另外noexcept也是一个运算符,后面接一个bool的参数:
void foo(void) noexcept;
void foo(void) noexcept(true);
上面两个函数是等价的,不过在vs中好像构成了重载,就是说两个都可以存在,编译能够通过。
7. 关键字constexpr。
constexpt关键字让表达式在编译器就是常量,因此可以用在一些只能是常量的地方。比如下面的例子:
constexpr int square(int x) {
return x * x;
}
int main() {
int a[square(3)]{};
return 0;
}
下面的例子中就移动要用constexpr来声明square,否在main()中的a数组定义时会报错。
8. lambda表达式。
它的格式有两种:
[...] {...}
还有更复杂一些的:
[...] (...) mutable throw语句 ->retType {...}
多出来的部分都是可选的。
[]部分称为capture,其实就是从包含该lambda表达式的函数中的参数,它们传到lambda表达式的方式两种,一种是传值,一种是传引用。用=来表示传值,或者不写也表示传值;用&来表示传引用,传引用的时候要注意的是这个值会在不同的阶段改变,随之而来的是lambda表达式的这个变量也变了。
下面是几个例子:
int main() {
auto l = [] {
return 42;//不用指定类型,那么编译器就自动推断,比如这里就是int
};//分号不能忘
cout << l() << endl;//l后面的()不能忘,因为它是函数对象,打印42
/*l = []() -> double {
return 42;
};*/ //两个lambda类型不一样,所以不能这么用
auto ll = []() -> double {//指定了返回类型,这个时候就一定要把()也写上,不然会报错
return 42;
};
cout << ll() << endl;//打印42
int x = 0;
int y = 0;
auto lll = [x, &y]() {
cout << "x: " << x << " " << "y: " << y << endl;
++y;
//++x;这么写直接报错
};
x = y = 1;
lll();//打印x: 0 y: 1,注意前面的y=1有生效。
lll();//打印x: 0 y: 2
//使用mutable可以让传值变量也可以改变:
int z = 0;
auto llll = [z]() mutable {
++z;
cout << "z: " << z << endl;
};
z = 100;//但是无论是不是mutable,这个赋值对于llll都没有用
llll();//打印z: 1
}
9. 关键字decltype。
这个关键字让编译器能够得到表达式的类型。例如:
int main() {
std::map<std::string, float> coll;
decltype(coll)::value_type elem;
}
10. c++11新增的类型:char16_t / char32_t / long long / unsigned long long / std::nullptr_t。
11. c++会在main()中定义一个隐式的return 0。
所有main中不写return也可以。