C++11 新特性整理(2)
基于范围的or循环:
C++中对于for循环的写法:
1. 借助容器的迭代器完成
2. <algorithm>中的for_each算法:
3. 基于范围的for循环
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <memory>
#include <algorithm>
#include <vector>
void func(int n)
{
std::cout << n << " ";
}
int main()
{
std::vector<int> v {1,2,3,4,5,6};
// 基于迭代器
for (auto iter = v.begin(); iter != v.end(); iter++)
{
std::cout << *iter << " ";
}
std::cout << std::endl;
// 基于algorithm中的for_each
std::for_each(v.begin(), v.end(), func);
std::cout << std::endl;
// 基于范围的for循环
for (auto elem : v)
{
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
基于范围的for循环实际上是普通for循环的语法糖,它会在循环开始前就确定迭代的范围,而不是在每次迭代之后调用end()
如何让自定义的类型也能够支持基于范围的for循环?
基于范围的for循环实际上是普通for循环的语法糖,它需要查找到容器的begin()迭代器和end()迭代器,所以对于自定义类型来说,分别实现begin()和end()即可。
可调用对象:
C++中可调用对象定义:
1. 函数指针
2. 具有operator()成员函数的类对象
3.可以转换为函数指针的类对象
4. 类成员指针
具有operator()成员函数的类对象称之为仿函数,因此,需要明确两点:
1 仿函数不是函数,它是个类;
2 仿函数重载了()运算符,使得可以像函数那样子调用(代码的形式好像是在调用函数)。
上面对可调用类型的定义并没有包括函数类型或者函数引用(只有函数指针),因为函数类型不能直接用来定义对象,而函数引用,可以看作一个const类型的函数指针。
C++中可调用对象虽然有比较统一的操作形式,但是定义的方法却很多,这样会使得使用一个统一的方法保存或者传递可调用对象时,就会十分繁琐。C++11提供了std::function和std::bind统一了可调用对象的各种操作。
可调用对象的包装器
std::function是可调用对象的包装器,它是一个类模板。通过指定它的模板参数,可以用同一的方式处理函数,函数对象,函数指针,并允许保存和延迟它们的执行。
例如:
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <functional>
void func() // 普通函数
{
std::cout << __FUNCDNAME__ << std::endl;
}
class myex // 类 静态成员
{
public:
static int mymethod(int x)
{
std::cout << __FUNCDNAME__ << "(" << x << ")->" << std::endl;
return x;
}
};
class Bar // 具有operator()成员函数
{
public:
int operator()(int x)
{
std::cout << __FUNCDNAME__ << "(" << x << ")->" << std::endl;
return x;
}
};
int main()
{
// std::function
std::function<void(void)> fr1 = func; // 包装普通函数
fr1(); // 调用
// 包装类的静态成员函数
std::function<int(int)> fr2 = myex::mymethod;
std::cout << fr2(12) << std::endl;
// 包装仿函数
Bar bar;
fr2 = bar; // 赋值操作
std::cout << fr2(22) << std::endl;
return 0;
}
运行输出:
std::function填入适合的函数签名(即一个函数类型,只需包含返回值类型和参数列表)之后,他就变成一个可以容纳所有这一类调用方式的函数包装器。
std::function作为回调函数
回调函数就是一个通过函数指针调用的函数。如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
回调函数机制:
1、定义一个函数(普通函数即可);
2、将此函数的地址注册给调用者;
3、特定的事件或条件发生时,调用者使用函数指针调用回调函数。
(https://blog.csdn.net/yidu_fanchen/article/details/80513359)
现在,std::function可以替代函数指针,引用在回调函数:
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <functional>
class A
{
std::function<void(void)> callback_; //回调函数
public:
A(const std::function<void(void)>& f) : callback_(f) // 初始化callback_
{
}
void use_f()
{
callback_(); // 调用回调函数
}
};
// 定义回调函数 仿函数
class foo
{
public:
void operator()()
{
std::cout << __FUNCDNAME__ << std::endl;
}
};
int main()
{
foo foo_;
A a(foo_);
a.use_f(); // 调用回调函数
return 0;
}
所以,可以用std::function替代函数指针,作为参数传递到其他的函数中:
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <functional>
void call_even(int x, const std::function<void(int)>& f) // 作为参数
{
if (x % 2 == 0)
{
f(x);
}
}
void print_f(int x)
{
std::cout << x << " ";
}
int main()
{
for (int i = 0; i < 10; i++)
{
call_even(i, print_f);
}
std::cout << std::endl;
return 0;
}
std::bind绑定器:
std::bind用来将可调用对象和参其数一起进行绑定,绑定后的结果可以使用std::function进行保存,它的作用可以概括为:
1.将可调用对象与其参数绑定到一起形成一个新的可调用对象
2.将多元的可调用对象进行转换(绑定某个或者某一些参数)
例如:
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <functional>
void call_even(int x, const std::function<void(int)>& f) // 作为参数
{
if (x % 2 == 0)
{
f(x); // 第一个参数x
}
}
void print_f(int x)
{
std::cout << x << " ";
}
void printf_f2(int x)
{
std::cout << x+2 << " ";
}
int main()
{
{
std::function<void(int)> fr = std::bind(print_f, std::placeholders::_1); // 占位符,这个位置将在函数调用时被第一个传入的参数取代
for (int i = 0; i < 10; i++)
{
call_even(i, fr);
}
std::cout << std::endl;
}
// 通过绑定不同的函数呈现出不同的行为
{
std::function<void(int)> fr = std::bind(printf_f2, std::placeholders::_1); // 占位符,这个位置将在函数调用时被第一个传入的参数取代
for (int i = 0; i < 10; i++)
{
call_even(i, fr);
}
std::cout << std::endl;
}
return 0;
}
占位符使得std::bind大的应用非常灵活:占位符表示这个位置将在函数调用时被第几个传入的参数取代:
可以举一个例子:
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <functional>
void func(int x, int y)
{
std::cout << x << " " << y << std::endl;
}
int main()
{
std::bind(func, std::placeholders::_1, 3)(2, 4); // 输出2 3
std::bind(func, std::placeholders::_2, std::placeholders::_1)(1, 2, 3); // 输出2 1
std::bind(func, std::placeholders::_2, std::placeholders::_3)(1, 2, 3); // 输出2 3
return 0;
}
将类成员函数的指针和对象进行绑定,构造成一个仿函数(可调用对象),也可以将类中的数据成员和对象进行绑定,然后包装成一个可调用类型:
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <functional>
class A
{
public:
int i_ = 0;
void printf(int x, int y)
{
std::cout << x << " " << y << std::endl;
}
};
int main()
{
A a;
std::function<void(int, int)> fr = std::bind(&A::printf, &a, std::placeholders::_1, std::placeholders::_2); // 将类方法和对象绑定
fr(1, 2);
// 还可以绑定类的数据成员
std::function<int&(void)> fi = std::bind(&A::i_, &a);
fi() = 23; // 直接改变对象的数据成员的值
return 0;
}
C++11中的lambda表达式:
lambda表达式定义了一个你们匿名函数、,并且可以捕获一定范围内的变量。lambda表达式的语法如下:
[capture] (paras) opt->ret { body; }
capture: 捕获列表
paras: 参数
opt: 函数选项
ret: 返回值类型
body: 函数体
例如:
int main()
{
auto f = [](int a)->int {return 2 * a; };
std::cout << f(1) << std::endl;
return 0;
}
C++允许省略lambda返回值的返回值定义:
int main()
{
auto f = [](int a){return 2 * a; };
std::cout << f(1) << std::endl;
return 0;
}
同时,再在没有参数的情况下,lamda表达式的参数列表也是可以省略的:
int main()
{
auto f = []{return 2; };
std::cout << f() << std::endl;
return 0;
}
lambda的捕获列表可以捕获一定范围内的变量:
1. 【】表示不捕获任何变量
2. 【&】捕获外部作用域中的所有变量,并作为引用在函数体中使用(按引用捕获)
3. 【=】捕获外部作用域中的所有变量,并作为副本在函数体中使用(按值捕获)
4. 【=,&foo】按值捕获外部作用域中的所有变量,并且按引用捕获foo变量
5. 【bar】按值只捕获bar变量
6.【this】捕获当前类中的this指针,让lambda表达式拥有和当前类成员函数同样的访问权限
例如:
class A
{
public:
int i_ = 0;
void func(int x, int y)
{
auto x1 = []() {return i_; }; // error 没有捕获任何外部的变量
auto x2 = [this]() {return i_; }; // right
auto x3 = [this]() {return i_ + x + y; }; // error,没有捕获外部变量x,y
auto x5 = [this, x, y]() {return i_ + x + y; }; // right
auto x6 = [=]() {return i_ + x + y; }; //捕获所有外部变量
auto x7 = [&]() {return i_ + x + y; }; //捕获所有外部变量
}
};
lambda表达式可以非常方便的实现闭包,使得程序更加灵活:
int main()
{
std::vector<int> v;
int cnt = std::count_if(v.begin(), v.end(), [](int x) {return x > 10; });
int cnt = std::count_if(v.begin(), v.end(), [](int x) {return x < 10; });
int cnt = std::count_if(v.begin(), v.end(), [](int x) {return x > 10 && x<20; });
return 0;
}
---------------------------------------------------分割线---------------------------------------------------------