1.列表初始化
C++98中,标准允许使用{}对结构体和数组进行初始化。C++11扩大了列表初始化的使用范围,使其可以用于所有的自定义类型和内置类型,使用初始化列表时,可添加等号(=),也可不添加,但是建议添加等号,这样可读性更高。
#include <iostream>
#include <vector>
#include <list>
#include <map>
int main()
{
int a1 = 1;
int a2(1);
int a3 = { 1 };
//不建议这样写,代码没有可读性
int a4{ 1 };
vector<int> v1 = { 1, 2 };
list<double> lt = { 1.1, 1.3, 3.14 };
map<int, int> countMap = { {1, 2}, {2, 3}, {3, 4} };
return 0;
}
原因是C++11的STL容器中都加入了一个initializer list的构造函数
所以C++11中用{}括起来的列表叫做initializer list。
所以通过迭代器直接遍历整个initializer list ,就可以构造一段区间。
初始化列表中的pair类型为pair<const char*, const char*>,而countMap中pair的类型为pair<string, string>,两个类型不一致,怎么列表初始化呢
pair的构造和赋值是函数模板,只要类型可以转换就可以构造/赋值
2.声明
auto:自动推导类型,必须显示初始化,编译器将根据初始化的值自动推导类型
int main()
{
map<string, string> countMap = { {"string", "字符串"}, {"sort", "排序"} };
map<string, string>::iterator it1 = countMap.begin();
auto it2 = countMap.begin();
return 0;
}
decltype:将变量的类型声明为表达式指定的类型
nullptr:C++中NULL被定义为字面量0,就可能带来一些问题,所以新增了nullptr,用与表示空指针。
3.右值引用和移动语义
左值:变量,表达式或函数传引用返回
右值:字面量,表达式或函数的返回值
区别:
左值可以取地址,右值不能取地址
左值引用引用左值,但const左值引用可以引用右质
右值引用引用右值,但右值引用可以引用move之后的左值
引用的本质:减少拷贝
左值引用:
1.解决了自定义类型传参时的拷贝问题
2.解决了一部分返回对象拷贝问题(出了作用域,对象生命周期还在)
未解决问题:
返回临时对象的拷贝问题(出了作用域,对象的生命周期就到了,只能传值返回)
所以为了解决该问题,C++11提出了右值引用
C++11中把右值分为两类:
1.纯右值(内置类型右值)
2.将亡值(自定义类型右值)
同时C++11中的各个容器都新增了两个默认成员函数,移动构造和移动赋值。移动构造和移动赋值的本质是把自定义类型将亡值的资源进行转移,这样右值引用的移动语义解决了返回临时对象的拷贝问题。
移动构造:
- 我们不写,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。编译器会自动生成一个默认移动构造。
- 默认生成的移动构造函数,对于内置类型成员进行值拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
- 我们自己实现了,编译器就不会默认生成了
移动赋值
- 我们不写,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。
- 默认生成的移动构造函数,对于内置类型成员进行值拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。
- 我们自己实现了,编译器就不会默认生成了
模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。 模板的万能引用提供了能够接收左值引用和右值引用的能力。
右值在被右值引用之后,右值的属性是左值,因为要实现移动构造和移动赋值的资源转移,所以右值在被右值引用之后,右值的属性是左值。
4.可变参数模板
虽然可变参数模板在源代码级别看起来可能有些复杂,但它们在编译时会被完全展开,并由编译器处理,但这一展开过程对我们开说来说是不可见的。但是对于编译器来讲,就相当于传递了0到多个的参数而已。
5.lambda
ambda表达式书写格式:[] () mutable -> return_type { statement
}
ambda表达式各部分说明
- [] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来
判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
[var]:表示值传递方式捕捉变量var
[=]:表示值传递方式捕获所有父作用域中的变量(包括this)
[&var]:表示引用传递捕捉变量var
[&]:表示引用传递捕捉所有父作用域中的变量(包括this)
[this]:表示值传递方式捕捉当前的this指针
- ():参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以
连同()一起省略 - mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
- ->return_type:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
- {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。
注意:
在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。
在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。
6.包装器
function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。可以包装任何可调用目标(包括普通函数、Lambda表达式、函数对象或绑定表达式等),并且可以用作函数的通用类型。这使得你能够将不同的可调用对象以统一的方式传递、存储和使用。
#include <functional>
int func(int x, int y)
{
return x + y;
}
struct Sub
{
int operator()(int x, int y)
{
return x - y;
}
};
class X
{
public:
static int mul(int x, int y)
{
return x * y;
}
int div(int x, int y)
{
return x / y;
}
};
int main()
{
function<int(int, int)> add = func;
function<int(Sub*, int, int)> sub = &Sub::operator();
function<int(int, int)> mul = X::mul;
function<int(X*, int, int)> div = &X::div;
Sub s;
X x;
cout << add(1, 2) << endl;
cout << sub(&s, 1, 2) << endl;
cout << mul(1, 2) << endl;
cout << div(&x, 1, 2) << endl;
return 0;
}
int main()
{
//function<int(int, int)> add = func;
//function<int(Sub*, int, int)> sub = &Sub::operator();
//function<int(int, int)> mul = X::mul;
//function<int(X*, int, int)> div = &X::div;
//Sub s;
//X x;
//cout << add(1, 2) << endl;
//cout << sub(&s, 1, 2) << endl;
//cout << mul(1, 2) << endl;
//cout << div(&x, 1, 2) << endl;
Sub s;
X x;
//bind
function<int(int, int)> add = func;
//_1为函数的第一个参数,_2为第二个参数,以此类推
function<int(int, int)> sub = bind(&Sub::operator(), &s, placeholders::_1, placeholders::_2);
function<int(int, int)> mul = X::mul;
function<int(int, int)> div = bind(&X::div, &x, placeholders::_1, placeholders::_2);
return 0;
}