这一节介绍一些C++11个人认为比较常用的部分
文章目录
- 1.{}列表初始化
- 2.initializer_list
- 3.auto、decltype、nullptr关键字
- 4.范围for
- 5.左值引用、右值引用、万能引用(完美转发)
- 6.lambda表达式
- 7.新的类功能
- 8.可变参数模板
- 9.包装器
1.{}列表初始化
C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。
C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。
2.initializer_list
首先有了initializer_list之后,对于STL的container的初始化就方便多了.
std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。也可以作为operator=的参数,这样就可以用大括号赋值。
3.auto、decltype、nullptr关键字
在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。
C++11中废弃auto原来的用法,将其用于实现自动类型推断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型。
关键字decltype将变量的类型声明为表达式指定的类型。
由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示
整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。所以nullptr就不用多说了!!
4.范围for
5.左值引用、右值引用、万能引用(完美转发)
简单说左值可以取地址,右值不可以取地址!
左值是一个表达式,右值也是一个表示数据的表达式,如:字面常量、表达式返回值等等!
什么是左值?什么是左值引用?
①左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左 值,不能给他赋值,但是可以取它的地址。
②左值引用就是给左值的引用,给左值取别名。
什么是右值?什么是右值引用?
①右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能 取地址。
②右值引用就是对右值的引用,给右值取别名。右值引用的属性是左值!!! 因为右值本身是不能改变的,属性不是左值的话就没法移动赋值和移动构造!!
左值引用-----拷贝构造
右值引用-----移动构造
构成函数重载!
不是特殊情况,不要随便把左值变成右值,否则会导致资源被转移,原本的值没了!如下图所示:
上面效率提升,针对的是自定义类型的深拷贝的类,因为深拷贝的类才有转移资源的移动系列函数;
对于内置类型,和浅拷贝自定义类型(比如日期类),没有移动系列函数
万能引用:
6.lambda表达式
lambda本质是一个函数,其表达式主要有五种形式:
①[capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda 函数使用。
②(parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以 连同()一起省略
③mutable:默认情况下可以省略,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
④->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推 导。
⑤{statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获 到的变量。
lambda的使用如下:
lambda的原理类似范围for,lambda编译时,编译器会生成对应的仿函数!
lambda使用起来比仿函数更加方便!
lambda最常用的三种方式:
7.新的类功能
原来C++类中,有6个默认成员函数:
- 构造函数
- 析构函数
- 拷贝构造函数
- 拷贝赋值重载
- 取地址重载
- const 取地址重载
最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。C++11 新增了两个:移动构造函数和移动赋值运算符重载。
①注意:如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。
默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
②注意:如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。
默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)
③如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。
强制生成默认函数的关键字default:
C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。
class Person
{
public:
Person(const char* name = "张三", int age = 18)
:_name(name)
, _age(age)
{}
// 强制生成
Person(const Person& p) = default;
Person& operator=(const Person & p) = default;
Person(Person&& p) = default;
Person& operator=(Person&& p) = default;
~Person()
{}
private:
bit::string _name;
int _age;
};
int main()
{
Person s1;
Person s2 = s1; // 默认拷贝构造
Person s3 = std::move(s1); // 默认移动构造
Person s4;
s4 = std::move(s2); // 默认移动赋值
return 0;
}
禁止生成默认函数的关键字delete:
如果能想要限制某些默认函数的生成,在C++98中,私有+只声明不实现,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。
// 这个类只能在堆上生成对象
class HeapOnly
{
public:
static HeapOnly* CreateObj()
{
return new HeapOnly;
}
// C++11
HeapOnly(const HeapOnly&) = delete;
// C++98 私有+只声明不实现
private:
//HeapOnly(const HeapOnly&);
HeapOnly()
{}
int _a = 1;
};
int main()
{
//HeapOnly ho1;
//HeapOnly* p1 = new HeapOnly;
HeapOnly* p2 = HeapOnly::CreateObj();
// 不能被拷贝,才能禁止
//HeapOnly obj(*p2);
return 0;
}
8.可变参数模板
下面就是一个基本可变参数的函数模板
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}
递归函数方式展开参数包:
逗号表达式展开参数包:
之前没有用过可变模板参数,所以没有使用过emplace_back,这里我们就来看看emplace_back,对比一下push_back:
因而,以后使用容器插入接口,推荐emplace系列
push系列/insert的接口,推荐使用emplace系列代替,其次emplace能用参数包就用参数包
9.包装器
①function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。
②除了function还有一个bind:
std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。
可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。
调用bind的一般形式:auto newCallable = bind(callable,arg_list);
其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给arg_list中的参数。
arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推。