5. 新的类功能
5.1 默认的移动构造和移动赋值
C++11在原有6个默认成员函数(构造函数、析构函数、拷贝构造函数、拷贝赋值重载、取地址重载、const取地址重载)基础上,新增了两个默认成员函数:
-
默认移动构造函数:如果没有自己实现移动构造函数,且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个,编译器会自动生成默认移动构造。
- 内置类型成员:按字节拷贝
- 自定义类型成员:如果该成员实现了移动构造就调用移动构造,否则调用拷贝构造
-
默认移动赋值重载函数:规则与移动构造类似,如果没有自己实现移动赋值重载,且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个,编译器会自动生成默认移动赋值。
class Person {
public:
Person(const char* name = "", int age = 0)
: _name(name), _age(age) {}
private:
bit::string _name; // 自定义类型
int _age; // 内置类型
};
int main() {
Person s1;
Person s2 = s1; // 拷贝构造
Person s3 = std::move(s1); // 移动构造
Person s4;
s4 = std::move(s2); // 移动赋值
}
5.2 成员变量声明时给缺省值
C++11允许在类定义中直接为成员变量指定缺省值,这些值将用于初始化列表:
class MyClass {
int x = 10; // 直接给缺省值
double y = 3.14;
};
5.3 default和delete
-
default:显式指示编译器生成默认版本
Person(Person&& p) = default; // 显式生成移动构造
-
delete:禁止编译器生成特定函数
Person(const Person& p) = delete; // 禁止拷贝构造
5.4 final与override
在继承和多态博客中已经讲解
6. STL中的一些变化
C++11为STL引入了多个新容器,其中最重要的是:
unordered_map
:基于哈希表的无序映射unordered_set
:基于哈希表的无序集合
其他新容器包括:
array
:固定大小数组forward_list
:单向链表
STL容器新增的重要接口:
-
右值引用和移动语义相关接口:
push_back
/insert
的右值引用版本emplace
系列接口
-
initializer_list
版本的构造函数:vector<int> v = {1, 2, 3, 4, 5}; map<string, string> dict = {{"sort", "排序"}, {"string", "字符串"}};
-
范围for循环
7. Lambda表达式
7.1 基本语法
Lambda表达式格式:[capture-list] (parameters) -> return-type { body }
[捕捉列表]
(参数列表)
->返回值类型
{函数体}
省略规则:
- 1、捕捉为空也不能省略
- 2、参数为空可以省略
- 3、返回值可以省略,可以通过返回对象自动推导
- 4、函数体不能省略
auto add = [](int x, int y) -> int { return x + y; };
cout << add(1, 2) << endl;
7.2 捕捉列表
-
显式捕捉:
[x]
:值捕捉x[&x]
:引用捕捉x[x, &y]
:混合捕捉
-
隐式捕捉:
[=]
:隐式值捕捉所有使用的变量[&]
:隐式引用捕捉所有使用的变量
-
混合捕捉:
[=, &x]
:x引用捕捉,其余值捕捉[&, x]
:x值捕捉,其余引用捕捉
-
mutable:允许修改值捕捉的变量,不影响外部变量(了解即可,不常用)
auto func = [x]() mutable { x++; };
7.3 Lambda的应用
Lambda常用于需要传递谓词的场景,如排序:
vector<Goods> v = {{"苹果", 2.1, 5}, {"香蕉", 3, 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._evaluate > g2._evaluate;
});
7.4 Lambda的原理
Lambda底层被编译器转换为仿函数类:
// Lambda表达式
auto r = [rate](double money, int year) { return money * rate * year; };
// 编译器生成的类似代码
class Lambda_1 {
public:
Lambda_1(double rate) : _rate(rate) {}
double operator()(double money, int year) {
return money * _rate * year;
}
private:
double _rate;
};
8. 包装器
8.1 function
std::function
是一个通用的函数包装器,可以包装各种可调用对象:
#include <functional>
int f(int a, int b) { return a + b; }
struct Functor {
int operator()(int a, int b) { return a + b; }
};
int main() {
// 包装函数指针
function<int(int, int)> f1 = f;
// 包装仿函数
function<int(int, int)> f2 = Functor();
// 包装Lambda
function<int(int, int)> f3 = [](int a, int b) { return a + b; };
// 包装成员函数
class Plus {
public:
static int plusi(int a, int b) { return a + b; }
double plusd(double a, double b) { return a + b; }
};
// 包装静态成员函数
// 成员函数要指定类域并且前⾯加&才能获取地址
function<int(int, int)> f4 = &Plus::plusi;
// 包装普通成员函数
// 普通成员函数还有⼀个隐含的this指针参数,所以绑定时传对象或者对象的指针过去都可以
function<double(Plus*, double, double)> f5 = &Plus::plusd;
}
8.2 bind
std::bind
用于绑定参数,可以调整参数顺序和数量:
#include <functional>
using namespace std::placeholders; // 引入_1, _2, _3...
int Sub(int a, int b) { return a - b; }
int main() {
// 调整参数顺序
auto sub1 = bind(Sub, _2, _1); // 第二个参数变为第一个
cout << sub1(5, 10) << endl; // 输出5
// 固定部分参数
auto sub2 = bind(Sub, 100, _1); // 第一个参数固定为100
cout << sub2(5) << endl; // 输出95
// 绑定成员函数
class Plus {
public:
double plusd(double a, double b) { return a + b; }
};
//绑定后函数调用不用传类对象,更方便
function<double(double, double)> f = bind(&Plus::plusd, Plus(), _1, _2);
}