C++11(中)
新增默认成员函数
C++11之前,默认成员函数有六个,构造函数,析构函数,拷贝构造,拷贝赋值重载,取地址重载,const 取地址重载。
C++11增加了 移动构造 和 移动赋值重载
如果类没有实现移动构造,且没有实现析构,拷贝,拷贝赋值重载中的任意一个,才会生成默认的移动构造。
默认的移动构造对内置类型进行按字节拷贝,自定义类型调用它的移动构造,该自定义类型没有实现移动构造就调用拷贝构造。
默认的移动赋值的生成和调用逻辑也类似。
新增关键字
default : 强制生成默认的成员函数
delete :强制禁用默认的成员函数
可变模板参数
C++11之后,模板可以接受多个参数
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}
//引用接收(万能引用)
template <class ...Args>
void ShowList(Args&&... args)
{}
实现一个打印函数
//递归的出口
void _my_print() {
;
}
//递归解析参数包 (args...)
template <class T, class... Args>
void _my_print(T x, Args... args) {
cout << x << endl;
_my_print(args...);
}
//打印函数,可以传入不同类型的参数
template <class... Args>
void my_print(Args... args) {
_my_print(args...);
}
调用一下
int main() {
my_print(2025,1.1, 'c', string("C++"));
return 0;
}
结果如下
上述代码是在编译时,递归式解析参数包
STL容器的 emplace_back 系列
STL的容器增加了 emplace_back 接口,功能和和 push_back 一样。
以 std::vector 为例,看一下接口
template< class... Args >
void emplace_back( Args&&... args );
emplace_back 支持了模板的可变参数,假如我用插入一个 pair<string, int> 类型的数据,emplace_back 只需传入 string 和 int 类型的参数即可,无需构造临时的 pair<string, int> 对象。
我们分析如下代码,来理解一下
vector<pair<string, int>> v;
v.push_back({ "西瓜", 2 });
v.emplace_back("西瓜", 2);
首先,{ “西瓜”, 2 } 是列表初始化,相当于隐式类型转换,会调用pair的构造函数来构造出pair<string, int>类型的临时对象,该临时对象属于右值,会调用vector的移动构造转移资源,所以
v.push_back({ “西瓜”, 2 }) 插入是pair的构造 + vector的移动构造
而 emplace_back 相当于传参给vector的构造,vector直接构造pair<string, int>类型对象并插入容器中。
v.emplace_back(“西瓜”, 2) 的插入只调用了vector的构造
所以从效率上说:对于浅拷贝的类或内置类型,emplace_back 效率更高。对于深拷贝的类,效率相差不大。
lambda表达式
lambda 表达式是一个匿名函数对象,语法如下
[捕捉列表](参数)mutable -> 返回值类型 {函数体};
lambda 表达式中很多内容可以省略
如下代码
auto func = []() {};
func();
写一个add函数,如下代码
//也可以不带返回值类型 [](int a, int b) { return a + b; };
auto add = [](int a, int b)->int {
return a + b;
};
cout << add(3, 4) << endl;
捕捉列表可以捕捉当前作用域的变量,加 & 符号表示传引用捕捉,不加表示传值捕捉(lambda表达式会拷贝一份新的副本)
如下代码,传值捕捉,新的副本具有 const属性
int main() {
int a = 3, b = 4;
//lambda表达式中的 a b 是 mian 函数a b 的一份拷贝
auto add = [a, b]() { return a + b; };
cout << add() << endl;
取消传值捕捉的 const 属性需要加 mutable 关键字
auto add = [a, b]() mutable { return a + b; } ;
传引用捕捉如下代码
int a = 3, b = 4;
auto add = [&a, &b]() mutable { a++; b++; };
add();
cout << a << b << endl;
其他的一些捕捉凡是如下,可以和上述的捕捉方式混合使用。
[=]:表示值传递方式捕获所有父作用域(包含lambda函数的语句块)中的变量(包括this)
[&]:表示引用传递捕捉所有父作用域中的变量(包括this)
[this]:表示值传递方式捕捉当前的this指针
function 包装器
function 的本质是类模板,用来包装一切可调用对象。
可调用对象包括:函数指针,仿函数,函数名,lambda表达式。
// 类模板原型如下
template <class T> function; // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
//Ret: 被调用函数的返回类型
//Args…:被调用函数的形参
std::function 的实例化需要指定返回值类型,和参数类型
function<返回值类型(参数类型...)> 对象名 = 可调用对象;
如下示例
int f(int a, int b)
{
return a + b;
}
std::function<int(int, int)> func1 = f;
//调用func1
func1(1, 2);
function 类模板重载了 () ,调用该对象可以和仿函数一样。
对于非静态成员函数,可调用对象需要 & 符号,静态成员函数不需要
class op {
public:
static int sub(int a, int b) {
return a - b;
}
int sum(int a, int b) {
return a + b;
}
};
function<int(op*, int, int)> f1 = &op::sum;
function<int(int, int)> f2 = op::sub;
std::bind
bind 是一个函数模板,接受一个可调用对象,生成一个新的可调用对象。bind 用来调整参数的顺序和个数。
// 原型如下:
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);
以上述的 op 类举例,绑定成员函数
op p;
function<int(int, int)> f3 = bind(&op::sum, &p, placeholders::_1, placeholders::_2);
function<int(int, int)> f4 = bind(&op::sum, p, placeholders::_1, placeholders::_2);
placeholders::_n ,是占位符,不需要绑定的参数用 placeholders::_n 表示。