C++11
目录
[5.2.泛左值(generalized value)](#5.2.泛左值(generalized value))
一、C++11的发展历史
C++的第二个重要版本
是从C++98开始的最重要更新

二、列表初始化
2.1.C++98中的{}
用于数组 、结构体的初始化
cpp
struct Point
{
int _x;
int _y;
};
int main()
{
int array1[] = { 1, 2, 3, 4, 5 };
int array2[5] = { 0 };
Point p = { 1, 2 };
return 0;
}
2.2.C++11中的{}
用于一切对象的初始化
(注:{}初始化也叫做列表初始化)
支持内置类型 ,自定义类型(类型转换,产生临时对象,优化为直接构造)
(注:{}初始化时,可以省略掉=)
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include <vector>
struct Point
{
int _x;
int _y;
};
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{
cout << "Date(int year, int month, int day)" << endl;
}
Date(const Date& d)
:_year(d._year)
,_month(d._month)
,_day(d._day)
{
cout << "Date(const Date& d)" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
//内置类型
int x1 = { 2 };
//自定义类型
//本质:
//用{2025, 1, 1}构造一个Date临时对象
//临时对象再去拷贝构造d1
//编译器优化后变成{2025, 1, 1}直接构造初始化
Date d1 = { 2025, 1, 1 };
//d2是{2024, 7, 25}构造的临时对象
//临时对象具有常性,需要const修饰
const Date& d2 = { 2024, 7, 25 };
//C++98支持单参数时类型转换
//也可以不用{}
Date d3 = { 2025 };//C++11
Date d4 = 2025;//C++98
//可以省略掉=
Point p1{ 1, 2 };
int x2{ 2 };
Date d6{ 2024, 7, 25 };
const Date & d7{ 2024, 7, 25 };
//不支持,只有{}初始化,才能省略=
//Date d8 2025;
vector<Date> v;
v.push_back(d1);
v.push_back(Date(2025, 1, 1));
//比起有名对象和匿名对象传参
//这里{}更有性价比
v.push_back({ 2025, 1, 1 });
return 0;
}

三、C++中的std::initializer_list
对于对象容器的初始化,用列表初始化不太方便
(比如:vector对象,用N个值去构造初始化,需要实现多个构造函数)
可以用std::initializer_list进行初始化

cpp
vector<int> v1 = { 1,2,3,4,5 };
vector<int> v2 = { 1,2,3,4,5,1,1,1,1,1 };
3.1.std::initializer_list的类
**本质:**底层开一个数组,将数据拷贝,内部有两个指针分别指向数组的开始和结束
(注:std::initializer_list支持迭代器遍历)

cpp
template<class T>
class vector {
public:
typedef T* iterator;
vector(initializer_list<T> l)
{
for (auto e : l)
push_back(e)
}
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endofstorage = nullptr;
};

3.2.std::initializer_list的使用
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<string>
#include<map>
using namespace std;
int main()
{
std::initializer_list<int> mylist = { 10, 20, 30 };
cout << sizeof(mylist) << endl;
//begin和end返回的值initializer_list对象中存的两个指针
//这两个指针的值跟i的地址跟接近,说明数组存在栈上
int i = 0;
cout << mylist.begin() << endl;
cout << mylist.end() << endl;
cout << &i << endl;
//直接构造,将实参传给形参,调用initialize_list
vector<int> v1({ 1,2,3,4,5 });
//构造临时对象+临时对象拷贝构造给v2+优化为直接构造
vector<int> v2 = { 1,2,3,4,5 };
//构造临时对象,临时对象具有常性,需要用const修饰
const vector<int>& v3 = { 1,2,3,4,5 };
// initializer_list版本的赋值支持
v1 = { 10,20,30,40,50 };
//pair对象的{}初始化和map的initializer_list构造结合到一起使用
map<string, string> dict = { {"sort", "排序"}, {"string", "字符串"} };
return 0;
}
四、右值引用和移动语义
4.1.左值和右值
**左值(lvalue):**left value / locate value(可取地址的对象)
数据的表达式(变量名 、解引用的指针)
具有持久状态,存储在内存中
可以出现在赋值符号的左边,也可以出现在右边,左值可以取地址
(注:定义时const修饰符后的左值,不能赋值,但可以取地址)
cpp
#include<iostream>
using namespace std;
int main()
{
//左值:可以取地址
//以下的p、b、c、*p、s、s[0]就是常见的左值
int* p = new int(0);
int b = 1;
const int c = b;
*p = 10;
string s("111111");
s[0] = 'x';
return 0;
}
**右值(rvalue):**right value / read value(不可寻址的对象)
数据的表达式(字面值常量 、存储在寄存器中的变量 、表达式求值过程中创建的临时对象)
可以出现在赋值符号的右边,但是不能出现在左边,右值不可以取地址
cpp
#include<iostream>
using namespace std;
int main()
{
// 右值:不能取地址
double x = 1.1, y = 2.2;
//10、x + y、fmin(x, y)、string("11111")都是常见的右值
10;
x + y;
fmin(x, y);
string("11111");
return 0;
}
4.2.左值引用和右值引用
**左值引用:**Type & r1 = x
**右值引用:**Type && rr1 = y
无论是左值引用还是右值引用,都是给对象取别名
左值引用给左值取别名
cpp
#include<iostream>
using namespace std;
int main()
{
int* p = new int(0);
int b = 1;
const int c = b;
*p = 10;
string s("111111");
s[0] = 'x';
//左值引用给左值取别名
int& r1 = b;
int*& r2 = p;
int& r3 = *p;
string& r4 = s;
char& r5 = s[0];
return 0;
}
右值引用给move**(左值)取别名**
cpp
#include<iostream>
using namespace std;
int main()
{
int* p = new int(0);
int b = 1;
const int c = b;
*p = 10;
string s("111111");
s[0] = 'x';
//右值引用不能直接引用左值
//右值引用可以引用:move(左值)
int&& rrx1 = move(b);
int*&& rrx2 = move(p);
int&& rrx3 = move(*p);
string && rrx4 = move(s);
//move的底层是强制类型转换:string && rrx5 = (string&&)s;
return 0;
}
**注:**move是一个函数模板,内部进行强制类型转换,涉及引用折叠

右值引用给右值取别名
cpp
#include<iostream>
using namespace std;
int main()
{
double x = 1.1, y = 2.2;
//右值引用给右值取别名
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
string && rr4 = string("11111");
return 0;
}
const左值引用给右值取别名
cpp
#include<iostream>
using namespace std;
int main()
{
double x = 1.1, y = 2.2;
//左值引用不能直接引用右值
//const左值引用可以引用右值
const int& rx1 = 10;
const double& rx2 = x + y;
const double& rx3 = fmin(x, y);
const string & rx4 = string("11111");
return 0;
}
**引用本身是左值:**左值引用的属性是左值,右值引用的属性也是左值
cpp
#include<iostream>
using namespace std;
int main()
{
//rr1的属性是左值
int&& rr1 = 10;
//左值可以被左值引用绑定
int& r6 = rr1;
//不能再被右值引用绑定
//int&& rrx6 = rr1;
//除非使用move(左值)
int&& rrx6 = move(rr1);
return 0;
}
**语法层面:**左值引用与右值引用都是取别名,不开空间
**汇编层面:**左值引用与右值引用的底层都是用指针实现
cpp
#include<iostream>
using namespace std;
int main()
{
int&& rr1 = 10;
int a = 20;
int& r2 = a;
return 0;
}

4.3.引用延长生命周期
右值引用: 为临时对象、匿名对象延长生命周期(读写)
const左值引用: 为临时对象、匿名对象延长生命周期(只读)
cpp
#include<iostream>
using namespace std;
int main()
{
std::string s1 = "Test";
//右值引用不能绑定左值
//std::string&& r1 = s1;
//const左值引用:延长s1+s2返回的临时对象string的生命周期,与r2相同
const std::string & r2 = s1 + s1;
//不能对const的引用修改
//r2 += "Test";
//右值引用:延长s1+s2返回的临时对象string的生命周期,与r3相同
std::string && r3 = s1 + s1;
//能对非const的引用修改
r3 += "Test";
std::cout << r3 << '\n';
return 0;
}
4.4.左值和右值的参数匹配
**C++98:**使用const左值引用作为函数形参,实参既可以传左值也可以传右值
**C++11:**分别重载左值引用,const左值引用,右值引用作为函数形参
- 实参为左值 ,匹配左值引用
- 实参为const左值 ,匹配const左值引用
- 实参为右值 ,匹配右值引用
cpp
#include<iostream>
using namespace std;
void f(int& x)
{
std::cout << "左值引用重载f(" << x << ")\n";
}
void f(const int& x)
{
std::cout << "const左值引用重载f(" << x << ")\n";
}
void f(int&& x)
{
std::cout << "右值引用重载f(" << x << ")\n";
}
int main()
{
int i = 1;
const int ci = 2;
//调用f(int&)
f(i);
//调用f(const int&)
f(ci);
//调用f(int&&)
f(3);//如果没有 f(int&&)重载则会调用f(const int&)
//调用f(int&&)
f(std::move(i));
//右值引用变量在用于表达式时是左值
int&& x = 1;
//调用f(int& x)
f(x);
//调用f(int&& x)
f(std::move(x));
return 0;
}

4.5.左值引用的使用场景
左值引用的优点
- 左值引用传函数形参,避免传值传参发生拷贝
- 左值引用传返回值,避免产生临时对象时拷贝
- 左值引用传返回值,可以修改实参和返回对象
左值引用的问题
当返回的对象为局部对象时,函数结束后这个对象被析构,造成野引用
无论是左值引用还是右值引用都无法解决这个问题,只能使用传值返回
cpp
#include<iostream>
using namespace std;
#include <vector>
class Solution
{
public:
//传值返回没有问题
string addStrings(string num1, string num2)
//左值引用返回会失败
string& addStrings(string num1, string num2)
//右值引用返回会报错
string&& addStrings(string num1, string num2)
{
string str;
int end1 = num1.size() - 1, end2 = num2.size() - 1;
int next = 0;
while (end1 >= 0 || end2 >= 0)
{
int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;
int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;
int ret = val1 + val2 + next;
next = ret / 10;
ret = ret % 10;
str += ('0' + ret);
}
if (next == 1)
{
str += '1';
}
reverse(str.begin(), str.end());
return str;
//即使右值引用使用move(左值)也会失败:return move(str)
}
};
int main()
{
string ret = Solution().addStrings("11111111111111111", "22222222222222");
cout << ret << endl;
return 0;
}
4.6.移动构造和移动赋值
**移动构造:**一种构造函数,与拷贝构造类似
**要求:**第一个参数是该类类型的引用(右值引用),额外的参数必须要有缺省值
**移动赋值:**一种赋值运算符重载,与拷贝赋值函数类似,构成函数重载
**要求:**第一个参数是该类类型的引用(右值引用)
只有像string/vector这样的深拷贝的类,或者包含深拷贝成员变量的类
移动构造和移动赋值才有意义,本质是窃取引用的右值对象的资源
而非像拷贝构造与拷贝赋值去拷贝资源,从而提高效率
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
#include<string.h>
#include<algorithm>
using namespace std;
namespace bit
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
cout << "string(char* str)-构造" << endl;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void swap(string& s)
{
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
//拷贝构造(左值)
string(const string& s)
:_str(nullptr)
{
cout << "string(const string& s) -- 拷贝构造" << endl;
reserve(s._capacity);
for (auto ch : s)
{
push_back(ch);
}
}
//移动构造(右值)
string(string&& s)
{
cout << "string(string&& s) -- 移动构造" << endl;
//交换:掠夺资源(右值引用的属性为左值)
swap(s);
}
string& operator=(const string& s)
{
cout << "string& operator=(const string& s) -- 拷贝赋值" << endl;
if (this != &s)
{
_str[0] = '\0';
_size = 0;
reserve(s._capacity);
for (auto ch : s)
{
push_back(ch);
}
}
return *this;
}
//移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动赋值" << endl;
swap(s);
return *this;
}
~string()
{
cout << "~string() -- 析构" << endl;
delete[] _str;
_str = nullptr;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
if (_str)
{
strcpy(tmp, _str);
delete[] _str;
}
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
const char* c_str() const
{
return _str;
}
size_t size() const
{
return _size;
}
private:
char* _str = nullptr;
size_t _size = 0;
size_t _capacity = 0;
};
}
int main()
{
//直接构造
bit::string s1("xxxxx");
//拷贝构造
bit::string s2 = s1;
//构造+移动构造,优化后直接构造
bit::string s3 = bit::string("yyyyy");
//移动构造
bit::string s4 = move(s1);
cout << "******************************" << endl;
return 0;
}

4.7.右值对象构造
**场景1:**只有拷贝构造,没有移动构造
vs2019 debug环境
**左边:**无优化,两次拷贝构造
**右边:**编译器优化,合二为一变为一次拷贝构造

**场景2:**既有拷贝构造,又有移动构造
vs2019 debug环境
**左边:**无优化,两次移动构造
右边: 编译器优化,合二为一变为一次移动构造

vs2019 release 和 vs2022 debug/release环境
将str对象的构造,str拷贝构造临时对象,临时对象拷贝构造ret对象,合三为一,变为直接构造

linux g++环境
编译时用g++ test.cpp -fno-elide-construtors关闭优化

**左值引用与右值引用的目的:**减少拷贝,提高效率
(注:左值引用还可以修改参数或者返回值,方便使用)
左值引用的局限性
部分函数返回场景,只能传值返回,不能左值引用返回
当前函数的局部对象,出了当前函数的作用域,生命周期结束后销毁
不能用左值引用返回,只能传值返回
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include <vector>
class Solution
{
public:
//这里的传值返回拷贝代价太大
vector<vector<int>> generate(int numRows)
{
vector<vector<int>> vv(numRows);
for (int i = 0; i < numRows; ++i)
{
vv[i].resize(i + 1, 1);
}
for (int i = 2; i < numRows; ++i)
{
for (int j = 1; j < i; ++j)
{
vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
}
}
return vv;
}
};
int main()
{
vector<vector<int>> ret = Solution().generate(100);
return 0;
}
**解决方案1:**不用返回值,用输出型参数解决(牺牲可读性)
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include <vector>
class Solution
{
public:
void generate(int numRows, vector<vector<int>>& vv)
{
vector<vector<int>> vv(numRows);
for (int i = 0; i < numRows; ++i)
{
vv[i].resize(i + 1, 1);
}
for (int i = 2; i < numRows; ++i)
{
for (int j = 1; j < i; ++j)
{
vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
}
}
}
};
int main()
{
vector<vector<int>> ret;
Solution().generate(100,ret);
return 0;
}
**解决方案2:**编译器的优化(非标准,不同的编译器优化可能不同)
**解决方案3:**新标准,新语法处理(右值引用和移动语义)
C++98:拷贝构造
无优化



一代优化



二代优化



C++98:拷贝构造+拷贝赋值
无优化



一代优化



二代优化



C++11:移动构造
无优化



一代优化



二代优化



C++11:移动构造+移动赋值
无优化



一代优化



二代优化



4.8.移动构造和移动赋值的意义
深拷贝的自定义类型
如:vector、string
实现移动构造和移动赋值有很大价值
浅拷贝的自定义类型
如:Date、pair<int,int>
不需要实现移动构造和移动赋值
五、类型分类
C++11后,对类型进行进一步划分

5.1.右值(rvalue)
纯右值(pure value)+ 将亡值(expiring value)
5.2.泛左值(generalized value)
左值(lvalue)+ 将亡值(expiring value)
5.3.纯右值(prvalue)
字面值常量、求值结果相当于字面值、不具名的临时对象
(比如:42、true、nullptr、str.substr(1,2)、str1 + str2传值返回函数调用、整型a、b、a+b、a++)
5.4.将亡值(xvalue)
返回右值引用的函数的调用表达式、转换为右值引用的转换函数的调用表达式
(比如:move(x)、static_cast<X&&>(x))
- 有名字的就是泛左值(glvalue)
- 有名字的、不能被move的是左值(lvalue)
- 有名字的、可以被move的是将亡值(xvalue)
- 无名字的、可以被move的是纯右值(prvalue)
六、引用折叠
6.1.引用折叠的规则
**C++11中无法定义引用的引用:**int& && r = i(直接报错)
使用模板或者typedef中的类型操作就可以构成引用的引用
cpp
int main()
{
typedef int& lref;
typedef int&& rref;
int n = 0;
lref& r1 = n; //r1 的类型是 int&
lref&& r2 = n; //r2 的类型是 int&
rref& r3 = n; //r3 的类型是 int&
rref&& r4 = 1; //r4 的类型是 int&&
return 0;
}
- 左值引用 + 左值引用/右值引用 == 左值引用
- 右值引用 + 右值引用 == 右值引用
6.2.函数模板参数为左值引用
cpp
// 由于引用折叠限定,f1实例化以后总是一个左值引⽤
template<class T>
void f1(T& x)
{}
int main()
{
//没有折叠->实例化为void f1(int& x)
f1<int>(n);
f1<int>(0);//报错
//折叠->实例化为void f1(int& x)
f1<int&>(n);
f1<int&>(0);//报错
//折叠->实例化为void f1(int& x)
f1<int&&>(n);
f1<int&&>(0);//报错
//折叠->实例化为void f1(const int& x)
f1<const int&>(n);
f1<const int&>(0);
//折叠->实例化为void f1(const int& x)
f1<const int&&>(n);
f1<const int&&>(0);
return 0;
}
6.3.函数模板参数为右值引用
传递左值时,就是左值引用
传递右值时,就是右值引用
又称为万能引用
cpp
// 由于引用折叠限定,f2实例化后可以是左值引用,也可以是右值引用
template<class T>
void f2(T&& x)
{}
int main()
{
//没有折叠->实例化为void f2(int&& x)
f2<int>(n);//报错
f2<int>(0);
//折叠->实例化为void f2(int& x)
f2<int&>(n);
f2<int&>(0);//报错
//折叠->实例化为void f2(int&& x)
f2<int&&>(n);//报错
f2<int&&>(0);
return 0;
}
在Function(T&& t) 函数模板中
- 假设实参为int右值、模板参数T推导为int
- 假设实参为int左值、模板参数T推导为int&
结合引用折叠
- 实参是左值、实例化左值引用版本形参的Function
- 实参是右值、实例化右值引用版本形参的Function
cpp
template <class T>
void Function(T && t)
{
int a = 0;
T x = a;
//x++;
cout << &a << endl;
cout << &x << endl << endl;
}
int main()
{
//10是右值 -> 推导出T为int -> 模板实例化为void Function(int&& t)
Function(10);//右值
int a;
//a是左值 -> 推导出T为int& -> 引用折叠 -> 模板实例化为void Function(int& t)
Function(a);//左值
//std::move(a)是右值 -> 推导出T为int -> 模板实例化为void Function(int&& t)
Function(std::move(a));//右值
const int b = 8;
//a是左值 -> 推导出T为const int& -> 引用折叠 -> 模板实例化为void Function(const int&t)
//所以Function内部会编译报错,x不能++
Function(b);//const左值
//std::move(b)右值 -> 推导出T为const int -> 模板实例化为void Function(const int&&t)
//所以Function内部会编译报错,x不能++
Function(std::move(b));//const右值
return 0;
}
6.4.完美转发
**完美转发:**一个类模板,保证值类别与类型相一致
cpp
template <class _Ty>
_Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept
{
return static_cast<_Ty&&>(_Arg);
}
右值引用类型的变量,属性(值类别)为左值
当再去使用形参t作为实参去调用函数时,使用的是左值引用
所以决定调用哪个函数的并非实参的类型而是实参的值类别
cpp
void Fun(int& x)
{
cout << "左值引用" << endl;
}
void Fun(const int& x)
{
cout << "const 左值引用" << endl;
}
void Fun(int&& x)
{
cout << "右值引用" << endl;
}
void Fun(const int&& x)
{
cout << "const 右值引用" << endl;
}
template<class T>
void Function(T&& t)
{
Fun(t);
}
int main()
{
//10是右值 -> 推导出T为int -> 模板实例化为void Function(int&& t)
Function(10);//右值
int a;
// a是左值 -> 推导出T为int& -> 引用折叠 -> 模板实例化为void Function(int& t)
Function(a);//左值
// std::move(a)是右值 -> 推导出T为int -> 模板实例化为void Function(int&& t)
Function(std::move(a));//右值
const int b = 8;
// a是左值 -> 推导出T为const int& -> 引用折叠 -> 模板实例化为void Function(const int&t)
Function(b); // const 左值
// std::move(b)右值 -> 推导出T为const int -> 模板实例化为void Function(const int&&t)
Function(std::move(b));//const 右值
return 0;
}

**注:**该段程序全为左值引用或者const 左值引用,使用完美转发就可以避免这种情况
cpp
void Fun(int& x)
{
cout << "左值引用" << endl;
}
void Fun(const int& x)
{
cout << "const 左值引用" << endl;
}
void Fun(int&& x)
{ cout << "右值引用" << endl;
}
void Fun(const int&& x)
{
cout << "const 右值引用" << endl;
}
template<class T>
void Function(T&& t)
{
Fun(forward<T>(t));
}
int main()
{
//10是右值 -> 推导出T为int -> 模板实例化为void Function(int&& t)
Function(10);//右值
int a;
// a是左值 -> 推导出T为int& -> 引用折叠 -> 模板实例化为void Function(int& t)
Function(a);//左值
// std::move(a)是右值 -> 推导出T为int -> 模板实例化为void Function(int&& t)
Function(std::move(a));//右值
const int b = 8;
// a是左值 -> 推导出T为const int& -> 引用折叠 -> 模板实例化为void Function(const int&t)
Function(b); // const 左值
// std::move(b)右值 -> 推导出T为const int -> 模板实例化为void Function(const int&&t)
Function(std::move(b));//const 右值
return 0;
}

push_back右值引用的使用场景
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include <assert.h>
#include <string.h>
#include <list>
#include <algorithm>
namespace bit
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
cout << "string(char* str)-构造" << endl;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void swap(string& s)
{
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
//拷贝构造(左值)
string(const string& s)
:_str(nullptr)
{
cout << "string(const string& s) -- 拷贝构造" << endl;
reserve(s._capacity);
for (auto ch : s)
{
push_back(ch);
}
}
//移动构造(右值)
string(string&& s)
{
cout << "string(string&& s) -- 移动构造" << endl;
//交换:掠夺资源(右值引用的属性为左值)
swap(s);
}
string& operator=(const string& s)
{
cout << "string& operator=(const string& s) -- 拷贝赋值" << endl;
if (this != &s)
{
_str[0] = '\0';
_size = 0;
reserve(s._capacity);
for (auto ch : s)
{
push_back(ch);
}
}
return *this;
}
//移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动赋值" << endl;
swap(s);
return *this;
}
~string()
{
cout << "~string() -- 析构" << endl;
delete[] _str;
_str = nullptr;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
if (_str)
{
strcpy(tmp, _str);
delete[] _str;
}
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
const char* c_str() const
{
return _str;
}
size_t size() const
{
return _size;
}
private:
char* _str = nullptr;
size_t _size = 0;
size_t _capacity = 0;
};
}
int main()
{
bit::string s1("1111111");//构造
list<bit::string> lt;
lt.push_back(s1);//拷贝构造
lt.push_back(move(s1));//移动构造
lt.push_back("2222222");//构造+移动构造
return 0;
}

万能引用+完美转发的使用场景
list.h
cpp
#pragma once
namespace bit
{
template < class T>
struct ListNode
{
ListNode<T>*_next;
ListNode<T>*_prev;
T _data;
ListNode() = default;
template<class X>
ListNode(X&& data = T())
:_next(nullptr)
,_prev(nullptr)
,_data(forward<X>(data))
{}
};
template <class T, class Ref, class Ptr>
struct ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T, Ref, Ptr> Self;
Node * _node;
ListIterator(Node * node)
:_node(node)
{}
Self & operator++()
{
_node = _node->_next;
return *this;
}
Ref operator*()
{
return _node->_data;
}
bool operator!=(const Self& it)
{
return _node != it._node;
}
};
template < class T>
class list
{
typedef ListNode<T> Node;
public:
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
void empty_init()
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
}
list()
{
empty_init();
}
template<class X>
void push_back(X&& x)
{
insert(end(), forward<X>(x));
}
template<class X>
iterator insert(iterator pos, X&& x)
{
Node * cur = pos._node;
Node * newnode = new Node(forward<X>(x));
Node * prev = cur->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
return iterator(newnode);
}
private:
Node * _head;
};
}
test.c
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include <assert.h>
#include <string.h>
#include <list>
#include <algorithm>
#include "List.h"
namespace bit
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
cout << "string(char* str)-构造" << endl;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void swap(string& s)
{
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
//拷贝构造(左值)
string(const string& s)
:_str(nullptr)
{
cout << "string(const string& s) -- 拷贝构造" << endl;
reserve(s._capacity);
for (auto ch : s)
{
push_back(ch);
}
}
//移动构造(右值)
string(string&& s)
{
cout << "string(string&& s) -- 移动构造" << endl;
//交换:掠夺资源(右值引用的属性为左值)
swap(s);
}
string& operator=(const string& s)
{
cout << "string& operator=(const string& s) -- 拷贝赋值" << endl;
if (this != &s)
{
_str[0] = '\0';
_size = 0;
reserve(s._capacity);
for (auto ch : s)
{
push_back(ch);
}
}
return *this;
}
//移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动赋值" << endl;
swap(s);
return *this;
}
~string()
{
cout << "~string() -- 析构" << endl;
delete[] _str;
_str = nullptr;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
if (_str)
{
strcpy(tmp, _str);
delete[] _str;
}
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
const char* c_str() const
{
return _str;
}
size_t size() const
{
return _size;
}
private:
char* _str = nullptr;
size_t _size = 0;
size_t _capacity = 0;
};
}
int main()
{
bit::list<bit::string> lt;
cout << "*************************" << endl;
bit::string s1("111111111111111111111");
lt.push_back(s1);
cout << "*************************" << endl;
lt.push_back(bit::string("22222222222222222222222222222"));
cout << "*************************" << endl;
lt.push_back("3333333333333333333333333333");
cout << "*************************" << endl;
lt.push_back(move(s1));
return 0;
}

七、可变模板参数
7.1.基本语法及原理
C++11支持可变数量参数的函数模板和类模板
参数类型可变,参数个数可变
可变数目的参数被称为参数包
**模板参数包:**零个或者多个模板参数
**函数参数包:**零个或者多个函数参数
cpp
template<class ...Args> void Func(Args... args) {}//传值
template<class ...Args> void Func(Args&... args) {}//左值引用
template<class ...Args> void Func(Args&&... args) {}//万能引用
**省略号:**指出模板参数或者函数参数的标识一个包
在模板参数列表中
class ...或者typename ...,指出接下来的参数表示零个或者多个类型列表
在函数参数列表中
类型名... ,指出接下表示零个或者多个形参对象列表
(注:函数参数包可以用左值引用或万能引用表示)
**原理:**与模板类似,去实例化对应类型和多个参数
**sizeof...():**计算计算参数包中参数的个数
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include <string>
//0 ~ N个参数
template <class ...Args>
void Print(Args&&... args)
{
cout << sizeof...(args) << endl;
}
int main()
{
double x = 2.2;
Print();//包里有0个参数
Print(1);//包里有1个参数
Print(1, string("xxxxx")); // 包里有2个参数
Print(1.1, string("xxxxx"), x); // 包里有3个参数
return 0;
}

没有可变模板参数的情况
cpp
// 原理1:编译本质这里会结合引⽤折叠规则实例化出以下四个函数
void Print();
void Print(int&& arg1);
void Print(int&& arg1, string&& arg2);
void Print(double&& arg1, string&& arg2, double& arg3);
// 原理2:更本质去看没有可变参数模板,我们实现出这样的多个函数模板才能支持
void Print();
template <class T1>
void Print(T1&& arg1);
template <class T1, class T2>
void Print(T1&& arg1, T2&& arg2);
template <class T1, class T2, class T3>
void Print(T1&& arg1, T2&& arg2, T3&& arg3);
// ...
//可变参数模板是类型泛化基础上叠加数量变化,让泛型编程更灵活
总结:
- **模板:**一个函数模板实例化出多个不同类型参数的函数
- **可变参数模板:**一个可变参数函数模板实例化出多个不同参数个数的模板
7.2.包扩展
解析出参数包的内容,只有在编译时才能扩展
cpp
template <class ...Args>
void Print(Args... args)
{
//可变参数模板编译时解析
//下面是运行获取和解析,所以不支持使用
cout << sizeof...(args) << endl;
for (size_t i = 0; i < sizeof...(args); i++)
{
cout << args[i] << " ";
}
}
**扩展方法1:**编译时递归
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
void ShowList()
{
cout << endl;
}
template<class T, class ...Args>
void ShowList(T&& x, Args&&... args)
{
cout << x << " ";
ShowList(args...);
}
template <class ...Args>
void Print(Args&&... args)
{
ShowList(args...);
}
int main()
{
double x = 2.2;
Print();//包里有0个参数
Print(1);//包里有1个参数
Print(1, string("xxxxx")); // 包里有2个参数
Print(1.1, string("xxxxx"), x); // 包里有3个参数
return 0;
}


**扩展方法2:**函数传参
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
template <class T>
const T & GetArg(const T & x)
{
cout << x << " ";
return x;
}
template <class ...Args>
void Arguments(Args... args)
{}
template <class ...Args>
void Print(Args... args)
{
//GetArg必须返回获得到的对象
//才能组成参数包给Arguments
Arguments(GetArg(args)...);
}
//本质可以理解为编译器编译时包的扩展模式
//将上面的函数模板扩展实例化为下面的函数
//void Print(int x, string y, double z)
//{
// Arguments(GetArg(x), GetArg(y), GetArg(z));
//}
int main()
{
Print(1, string("xxxxx"), 2.2);
return 0;
}
7.3.emplace接口


cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include "assert.h"
#include <list>
namespace bit
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
cout << "string(char* str)-构造" << endl;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void swap(string& s)
{
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
//拷贝构造(左值)
string(const string& s)
:_str(nullptr)
{
cout << "string(const string& s) -- 拷贝构造" << endl;
reserve(s._capacity);
for (auto ch : s)
{
push_back(ch);
}
}
//移动构造(右值)
string(string&& s)
{
cout << "string(string&& s) -- 移动构造" << endl;
//交换:掠夺资源(右值引用的属性为左值)
swap(s);
}
string& operator=(const string& s)
{
cout << "string& operator=(const string& s) -- 拷贝赋值" << endl;
if (this != &s)
{
_str[0] = '\0';
_size = 0;
reserve(s._capacity);
for (auto ch : s)
{
push_back(ch);
}
}
return *this;
}
//移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动赋值" << endl;
swap(s);
return *this;
}
~string()
{
cout << "~string() -- 析构" << endl;
delete[] _str;
_str = nullptr;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
if (_str)
{
strcpy(tmp, _str);
delete[] _str;
}
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
const char* c_str() const
{
return _str;
}
size_t size() const
{
return _size;
}
private:
char* _str = nullptr;
size_t _size = 0;
size_t _capacity = 0;
};
}
//emplace_back总体而言是更高效
//推荐使用emplace系列替代insert和push系列
int main()
{
list<bit::string> lt;
bit::string s1("111111111111");
bit::string s2("111111111111");
//传左值,跟push_back一样,拷贝构造
lt.emplace_back(s1);
cout << "*********************************" << endl;
lt.push_back(s2);
cout << "*********************************" << endl;
//右值,跟push_back一样,移动构造
lt.emplace_back(move(s1));
cout << "*********************************" << endl;
lt.push_back(move(s2));
cout << "*********************************" << endl;
//直接把构造string参数包往下传
//直接用string参数包构造string
//这里的效果是push_back做不到的
lt.emplace_back("111111111111");
cout << "*********************************" << endl;
lt.push_back("111111111111");//直接传参,隐式类型转换
cout << "*********************************" << endl;
list<pair<bit::string, int>> lt1;
//跟push_back一样
//构造pair + 拷贝/移动构造pair到list的节点中data上
pair<bit::string, int> kv("苹果", 1);
lt1.emplace_back(kv);
cout << "*********************************" << endl;
lt1.push_back(kv);
cout << "*********************************" << endl;
//跟push_back一样
lt1.emplace_back(move(kv));
cout << "*********************************" << endl;
lt1.push_back(move(kv));
cout << "*********************************" << endl;
//直接把构造pair参数包往下传
//直接用pair参数包构造pair
//这里的效果是push_back做不到的
lt1.emplace_back("苹果", 1);//传参数包,直接构造
cout << "*********************************" << endl;
lt1.push_back({ "苹果", 1 });//隐式类型转换,构造临时对象+移动构造
cout << "*********************************" << endl;
return 0;
}

总结:
emplace系列兼任push系列和insert系列的功能
部分场景下emplace 可以直接构造
push、insert 是构造+移动构造/拷贝构造
cpp
template < class... Args>
void emplace_back(Args&&... args)
{
insert(end(), std::forward<Args>(args)...);
}
// 原理:本质编译器根据可变参数模板⽣成对应参数的函数
/*void emplace_back(string& s)
{
insert(end(), std::forward<string>(s));
}
void emplace_back(string&& s)
{
insert(end(), std::forward<string>(s));
}
void emplace_back(const char* s)
{
insert(end(), std::forward<const char*>(s));
}
*/
八、新的类功能
8.1.默认的移动构造与移动赋值
C++11之前的类中,有6个默认成员函数
构造函数、析构函数、拷贝构造、赋值重载、取地址重载、const取地址重载
不写时,编译器会生成一个默认的函数
C++11之后的类中,新增了两个默认成员函数
移动构造函数、移动赋值运算符重载
如果没有实现析构函数、拷贝构造、赋值重载、移动构造中的任意一个
编译器就会自动生成一个默认移动构造函数
内置类型成员:执行逐成员按字节拷贝
自定义类型成员:如果实现移动构造、就调用移动构造,如果没有、就调用拷贝构造
(注:默认移动赋值与移动构造完全类似)
如果提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include "assert.h"
namespace bit
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
cout << "string(char* str)-构造" << endl;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void swap(string& s)
{
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
//拷贝构造(左值)
string(const string& s)
{
cout << "string(const string& s) -- 拷贝构造" << endl;
reserve(s._capacity);
for (auto ch : s)
{
push_back(ch);
}
}
//移动构造(右值)
string(string&& s)
{
cout << "string(string&& s) -- 移动构造" << endl;
//交换:掠夺资源(右值引用的属性为左值)
swap(s);
}
string& operator=(const string& s)
{
cout << "string& operator=(const string& s) -- 拷贝赋值" << endl;
if (this != &s)
{
_str[0] = '\0';
_size = 0;
reserve(s._capacity);
for (auto ch : s)
{
push_back(ch);
}
}
return *this;
}
//移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动赋值" << endl;
swap(s);
return *this;
}
~string()
{
cout << "~string() -- 析构" << endl;
delete[] _str;
_str = nullptr;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
if (_str)
{
strcpy(tmp, _str);
delete[] _str;
}
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
const char* c_str() const
{
return _str;
}
size_t size() const
{
return _size;
}
private:
char* _str = new char('/0');
size_t _size = 0;
size_t _capacity = 0;
};
}
class Person
{
public:
Person(const char* name = "", int age = 0)
:_name(name)
, _age(age)
{}
/*Person(const Person& p)
:_name(p._name)
,_age(p._age)
{}
Person& operator=(const Person& p)
{
if(this != &p)
{
_name = p._name;
_age = p._age;
}
return *this;
}
~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;
}

8.2.声明时给缺省值
成员变量声明是给缺省值是给初始化列表使用的
如果没有显示在初始化列表初始化
就会在初始化列表时用这个缺省值
8.3.default和delete
**default关键字:**控制要使用的默认函数
提供拷贝构造后就不会生成移动构造,使用default关键字显示指定移动构造生成
**delete关键字:**限制某些默认函数的生成
在函数声明加=delete,指示编译器不生成对应函数的默认版本
cpp
class Person
{
public:
Person(const char* name = "", int age = 0)
:_name(name)
, _age(age)
{}
Person(const Person& p)
:_name(p._name)
, _age(p._age)
{}
Person(Person&& p) = default;
//Person(const Person& p) = delete;
private:
bit::string _name;
int _age;
};
int main()
{
Person s1;
Person s2 = s1;
Person s3 = std::move(s1);
return 0;
}
8.4.final与override
**final关键字:**修饰类,让该类无法被继承;修饰虚函数,让该虚函数无法重写
**override关键字:**放在派生类中重写的虚函数后面,虚函数重写时报错
九、STL中的变化
9.1.新的容器

9.2.新的接口
右值引用、移动语义相关的push/insert/emplace系列的接口
移动构造、移动赋值
initializer_list版本的构造
容器的范围for遍历
十、lambda
10.1.lambda表达式语法
本质: 匿名函数对象
**特点:**可以定义在函数内部
在语法使用层中没有类型,一般用auto或者模板参数定义的对象去接收lambda对象
格式:[capture-list] (parameters) -> return type {function body}
**[capture-list]:**捕捉列表
**(parameters):**参数列表
如果不需要传参,可以同()一起省略
**-> return type:**返回值类型
在返回值类型明确的情况下,可以省略,由编译器对返回类型进行推导
**{function body}:**函数体
函数体为空不能省略
示例1:
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
int main()
{
//一个简单的lambda表达式
//[]:捕捉列表
//(int x, int y):参数列表
//->int:返回类型
//{return x + y; }:函数体
auto add1 = [](int x, int y)->int {return x + y; };
cout << add1(1, 2) << endl;
return 0;
}
示例2:
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
int main()
{
//捕捉为空也不能省略
//参数为空可以省略
//返回值可以省略,可以通过返回对象自动推导
//函数题不能省略
auto func1 = [] {cout << "hello bit" << endl; return 0; };
func1();
return 0;
}
示例3:
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
int main()
{
int a = 0, b = 1;
auto swap1 = [](int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
};
swap1(a, b);
cout << a << ":" << b << endl;
return 0;
}
10.2.lambda的应用
仿函数
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>
struct Goods
{
string _name; //名字
double _price; //价格
int _evaluate; //评价
// ...
Goods(const char* str, double price, int evaluate)
:_name(str)
,_price(price)
,_evaluate(evaluate)
{}
};
struct ComparePriceLess
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price < gr._price;
}
};
struct ComparePriceGreater
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price > gr._price;
}
};
int main()
{
vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3}, { "菠萝", 1.5, 4 } };
sort(v.begin(), v.end(), ComparePriceLess());
sort(v.begin(), v.end(), ComparePriceGreater());
return 0;
}
lambda
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>
struct Goods
{
string _name; //名字
double _price; //价格
int _evaluate; //评价
// ...
Goods(const char* str, double price, int evaluate)
:_name(str)
,_price(price)
,_evaluate(evaluate)
{}
};
int main()
{
vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3}, { "菠萝", 1.5, 4 } };
//价格升序
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._price < g2._price;
});
//价格降序
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._price > g2._price;
});
//评价升序
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._evaluate < g2._evaluate;
});
//评价降序
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._evaluate > g2._evaluate;
});
return 0;
}
10.3.捕捉列表
lambda表达式默认只能用lambda函数体 和参数列表中的变量
如果想要使用外层作用域中的变量,就需要进行捕捉
显示传值捕捉和传引用捕捉
写法:[x,y,&z]
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
//全局域的变量可以直接使用
//不需要捕捉
int y = 0;
//全局域也可以写lambda表达式
auto func2 = []()
{
y++;
};
int main()
{
//只能用当前lambda所在局部域和全局域(向上查找)
int a = 0, b = 1, c = 2, d = 3;
auto func1 = [&a, &b](int x)
{
//值捕捉的变量无法修改
a++;
//引用捕捉的变量可以修改
b++;
int ret = a + b + x + y;
return x;
};
cout << func1(1) << endl;
func2();
return 0;
}
隐式捕捉
- 在捕捉列表写一个=表示隐式值捕捉
- 在捕捉列表写一个&表示隐式引用捕捉
用到谁,捕捉谁
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
int main()
{
int a = 1, b = 2, c = 3,d = 4;
auto func2 = [=]
{
int ret = a + b + c;
return ret;
};
cout << func2() << endl;
auto func3 = [&]
{
a++;
c++;
d++;
};
func3();
cout << a << " " << b << " " << c << " " << d << endl;
return 0;
}
混合捕捉
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
int main()
{
int a = 0, b = 1, c = 2, d = 3;
auto func4 = [&, a, b]
{
//a++;
//b++;
c++;
d++;
return a + b + c + d;
};
func4();
cout << a << " " << b << " " << c << " " << d << endl;
auto func5 = [=, &a, &b]
{
a++;
b++;
//c++;
//d++;
return a + b + c + d;
};
func5();
cout << a << " " << b << " " << c << " " << d << endl;
return 0;
}
在参数列表后加mutable可以消除传值调用时变量的const属性
让传值调用的变量可以在函数体内被修改但出函数后还是原值
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
int main()
{
int a = 1, b = 2, c = 3,d = 4;
auto func7 = [=]()mutable
{
a++;
b++;
c++;
d++;
return a + b + c + d;
};
cout << func7() << endl;
cout << a << " " << b << " " << c << " " << d << endl;
return 0;
}
10.4.lambda原理
和范围for类似,编译后, 从汇编指令层的角度来看
- 范围for的底层是迭代器
- lambda的底层是仿函数对象
写了一个lambda表达式后,编译器会生成一个对应的仿函数类
lambda参数、返回类型、函数体,就是仿函数operator()的参数、返回类型、函数体
lambda的捕捉列表就是仿函数类的成员变量
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Rate
{
public:
Rate(double rate)
: _rate(rate)
{}
//仿函数
double operator()(double money, int year)
{
return money * _rate * year;
}
private:
double _rate;
};
int main()
{
double rate = 0.49;
// lambda
auto r2 = [rate](double money, int year)
{
return money * rate * year;
};
// 函数对象
Rate r1(rate);
r1(10000, 2);
r2(10000, 2);
auto func1 = []
{
cout << "hello world" << endl;
};
func1();
return 0;
}

注:<lambda_1>这个类型名称的规则是编译器自己定制的,保证不同的lambda不冲突
十一、包装器
11.1.function
cpp
template <class T>
class function; // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
**std::function:**是一个类模板、也是一个包装器
实例化的对象可以包装存储其他可调用对象
比如:函数指针、仿函数、lambda、bind表达式
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include<functional>
int f(int a, int b)
{
return a + b;
}
struct Functor
{
public:
int operator() (int a, int b)
{
return a + b;
}
};
class Plus
{
public:
Plus(int n = 10)
:_n(n)
{
}
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return (a + b) * _n;
}
private:
int _n;
};
int main()
{
//包装各种可调用对象
function<int(int, int)> f1 = f;
function<int(int, int)> f2 = Functor();
function<int(int, int)> f3 = [](int a, int b) { return a + b; };
cout << f1(1, 1) << endl;
cout << f2(1, 1) << endl;
cout << f3(1, 1) << endl;
return 0;
}
试题:逆波兰表达式求解
题目内容:
给你一个字符串数组 tokens
表示一个根据逆波兰表示法表示的算术表达式
请你计算该表达式,返回一个表示表达式值的整数
示例:
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
注意:
有效的算符为 '+'、'-'、'*' 和 '/'
每个操作数(运算对象)都可以是一个整数或者另一个表达式
两个整数之间的除法总是向零截断
表达式中不含除零运算
输入是一个根据逆波兰表示法表示的算术表达式
答案及所有中间计算结果可以用32位整数表示
传统方式
cpp
class Solution
{
public:
int evalRPN(vector<string>& tokens)
{
stack<int> st;
for (auto& str : tokens)
{
if (str == "+" || str == "-" || str == "*" || str == "/")
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
switch (str[0])
{
case '+':
st.push(left + right);
break;
case '-':
st.push(left - right);
break;
case '*':
st.push(left * right);
break;
case '/':
st.push(left / right);
break;
}
}
else
{
st.push(stoi(str));
}
}
return st.top();
}
};
function作为map的映射可调用对象的类型
cpp
class Solution
{
public:
int evalRPN(vector<string>& tokens)
{
stack<int> st;
map<string, function<int(int, int)>> opFuncMap =
{
{"+", [](int x, int y) {return x + y; }},
{"-", [](int x, int y) {return x - y; }},
{"*", [](int x, int y) {return x * y; }},
{"/", [](int x, int y) {return x / y; }}
};
for (auto& str : tokens)
{
if (opFuncMap.count(str))
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
int ret = opFuncMap[str](left, right);
st.push(ret);
}
else
{
st.push(stoi(str));
}
}
return st.top();
}
};
包装静态成员函数与普通成员函数
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include<functional>
int f(int a, int b)
{
return a + b;
}
struct Functor
{
public:
int operator() (int a, int b)
{
return a + b;
}
};
class Plus
{
public:
Plus(int n = 10)
:_n(n)
{
}
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return (a + b) * _n;
}
private:
int _n;
};
int main()
{
//包装静态成员函数
//成员函数要指定类域并且前面加&才能获取地址
function<int(int, int)> f4 = &Plus::plusi;
cout << f4(1, 1) << endl;
//包装普通成员函数
//普通成员函数还有一个隐含的this指针参数
//所以绑定时传对象或者对象的指针过去都可以
function<double(Plus*, double, double)> f5 = &Plus::plusd;
Plus pd;
cout << f5(&pd, 1.1, 1.1) << endl;
function<double(Plus, double, double)> f6 = &Plus::plusd;
cout << f6(pd, 1.1, 1.1) << endl;
cout << f6(pd, 1.1, 1.1) << endl;
function<double(Plus&&, double, double)> f7 = &Plus::plusd;
cout << f7(move(pd), 1.1, 1.1) << endl;
cout << f7(Plus(), 1.1, 1.1) << endl;
return 0;
}
- 普通函数调用:(*Fptr)(1,2);
- 成员函数调用:(obj.*Fptr)(1,2);
11.2.bind
cpp
simple(1)
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
with return type (2)
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
**bind绑定:**是一个函数模板,也是一个可调用对象的包装器、可以看作函数适配器
**作用:**调整参数个数和参数顺序

cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include<functional>
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
int Sub(int a, int b)
{
return (a - b) * 10;
}
int SubX(int a, int b, int c)
{
return (a - b - c) * 10;
}
class Plus
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
int main()
{
// bind本质:返回的一个仿函数对象
//调整参数顺序(不常用)
//_1代表第一个实参
//_2代表第二个实参
//...
auto sub1 = bind(Sub, _1, _2);
cout << sub1(10, 5) << endl;//50
auto sub2 = bind(Sub, _2, _1);
cout << sub2(10, 5) << endl;//-50
// 调整参数个数 (常⽤)
auto sub3 = bind(Sub, 100, _1);
cout << sub3(5) << endl;
auto sub4 = bind(Sub, _1, 100);
cout << sub4(5) << endl;
// 分别绑死第1、2、3个参数
auto sub5 = bind(SubX, 100, _1, _2);
cout << sub5(5, 1) << endl;
auto sub6 = bind(SubX, _1, 100, _2);
cout << sub6(5, 1) << endl;
auto sub7 = bind(SubX, _1, _2, 100);
cout << sub7(5, 1) << endl;
// 用于绑死一些固定参数
// 成员函数对象进行绑死,就不需要每次都传递了
function<double(Plus&&, double, double)> f6 = &Plus::plusd;
Plus pd;
cout << f6(move(pd), 1.1, 1.1) << endl;
cout << f6(Plus(), 1.1, 1.1) << endl;
function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);
cout << f7(1.1, 1.1) << endl;
return 0;
}
**示例:**计算复利
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include<functional>
using placeholders::_1;
int main()
{
// 计算复利的lambda
// 复利前一年的利息变成第二年的本金
// (10000 * 0.02 + 10000) * 0.02 + 10000 * 0.02 + 10000
// 参数:利率 本金 年限
auto func1 = [](double rate, double money, int year)->double
{
double ret = money;
for (int i = 0; i < year; i++)
{
ret += ret * rate;
}
return ret - money;
};
// 绑死一些参数,实现出持不同年华利率,不同金额和不同年份计算出复利的结算利息
function<double(double)> func3_1_5 = bind(func1, 0.015, _1, 3);
function<double(double)> func5_1_5 = bind(func1, 0.015, _1, 5);
function<double(double)> func10_2_5 = bind(func1, 0.025, _1, 10);
function<double(double)> func20_3_5 = bind(func1, 0.035, _1, 30);
cout << func3_1_5(1000000) << endl;
cout << func5_1_5(1000000) << endl;
cout << func10_2_5(1000000) << endl;
cout << func20_3_5(1000000) << endl;
return 0;
}
