cpp11新特性详解与应用

发表信息: by

Lambda表达式

构成

Lambda表达式可以在使用标准库的一些功能的时候(如sort)定制关于不同的数据结构的排序方式。 为了描述一个lambda,需要提供:
它的捕捉列表:即(除了形参之外)他可以使用的变量列表(”[&]” 在上面的记录比较例子中意味着“所有的局部变量都将按照引用的方式进行传递”)。如果不需要捕捉任何变量,则使用 [],[=]表示值传递。
(可选的)它的所有参数及其类型(例如: (int a, int b) )。
组织成一个块的函数行为(例如:{ return v[a].name < v[b].name; })。
(可选的)使用”返回值类型后置语法“来指明返回类型。但典型情况下,我们仅从return语句中去推断返回类型,如果没有返回任何值,则推断为void。

Lambda表达式与STL算法一起使用;

通过“函数体”后面的‘()’传入参数。

int n = [] (int x, int y) { return x + y; }(5, 4);

cout << n << endl;

/***************/
std::sort(s.begin(), s.end(), [](int a, int b) {
    return a > b;   
});

参考资料:

智能指针

我的见解比较浅薄,如果指针自己管理,容易忘记释放,会造成内存泄漏问题。智能指针的出现解放了程序员。

std boost 功能说明
unique_ptr unique_ptr 独占指针对象,并保证指针所指对象生命周期与其一致
shared_ptr shared_ptr 可共享指针对象,可以赋值给shared_ptr或weak_ptr。指针所指对象在所有的相关联的shared_ptr生命周期结束时结束,是强引用。
weak_ptr weak_ptr 它不能决定所指对象的生命周期,引用所指对象时,需要lock()成shared_ptr才能使用。

unique_ptr

为动态申请的内存提供异常安全 将动态申请内存的所有权传递给某个函数(不能给复制,只能移动)

可以用于:

  • 从某个函数返回动态申请内存的所有权
  • 在容器中保存指针

在那些要不是为了避免不安全的异常问题(以及为了保证指针所指向的对象都被正确地删除释放),我们不可以使用内建指针的情况下,我们可以在容器中保存unique_ptr以代替内建指针

//文件 test-1.cpp
#include <memory>
#include <iostream>

using namespace std;

int main()
{
    unique_ptr<int> up1(new int(11));
    unique_ptr<int> up2 = up1;   //! 编译时会出错 [1]

    cout << *up1 << endl;
    unique_ptr<int> up3 = move(up1);  //! [2]
    cout << *up3 << endl;
    if (up1)
        cout << *up1 << endl;

    up3.reset();  //! [3]
    up1.reset();

    shared_ptr<string> sp1(make_shared<string>("Hello"));
    shared_ptr<string> sp2 = sp1;
    cout << "*sp1:" << *sp1 << endl;
    cout << "*sp2:" << *sp2 << endl;
    sp1.reset();
    cout << "*sp2:" << *sp2 << endl;

    weak_ptr<string> wp = sp2; //! [4]
    cout << "*wp.lock():" << *wp.lock() << endl;
    sp2.reset();
    cout << "*wp.lock():" << *wp.lock() << endl;  //! 运行时会出错
    return 0;
}
//编译命令: g++ -std=c++11 test-1.cpp
  • [1]: unique_ptr 是禁止复制赋值的,始终保持一个 unique_ptr 管理一个对象。
  • [2]: unique_ptr 虽然不能赋值,但可以通过 move() 函数转移对象的所有权。一旦被 move() 了,原来的 up1 则不再有效了。
  • [3]: reset() 可以让 unique_ptr 提前释放指针。
  • [4]: 由 shared_ptr 构造一个 weak_ptr.

shared_ptr与weak_ptr

当 shared_ref_cnt 被减为0时,自动释放 ptr 指针所指向的对象。当 shared_ref_cnt 与 weak_ref_cnt 都变成0时,才释放 ptr_manage 对象。
如此以来,只要有相关联的 shared_ptr 存在,对象就存在。weak_ptr 不影响对象的生命周期。当用 weak_ptr 访问对象时,对象有可能已被释放了,要先 lock()。

weak_ptr可以保存一个“弱引用”,指向一个已经用shared_ptr进行管理的对象。为了访问这个对象,一个weak_ptr可以通过shared_ptr的构造函数或者是weak_ptr的成员函数lock()转化为一个shared_ptr。当最后一个指向这个对象的shared_ptr退出其生命周期并且这个对象被释放之后,将无法从指向这个对象的weak_ptr获得一个shared_ptr指针,shared_ptr的构造函数会抛出异常,而weak_ptr::lock也会返回一个空指针。

参考资料:

函数式编程

algorigthm头文件中定义了几个很有用的方法;
相应代码见: cpp11_21_for_cppFunctionProm.cpp

for_each

对于一些collection(vector,list,set,map)可以对其中每个元素执行后面的函数;

//下面是函数式编程里面的for_each 
template<typename Collection,typename Func>
void for_each(Collection const &collection,Func func)
{
	std::for_each(collection.begin(),collection.end(),func);
}
//////////////使用///////////////
auto lambda_echo = [](int i ) { std::cout << i << std::endl; };  
std::vector<int> col{20,24,37,42,23,45,37};
for_each(col,lambda_echo);

transform实现map

transform。该算法用于实行容器元素的变换操作。有如下两个使用原型,一个将迭代器区间[first,last)中元素,执行一元函数对象op操作,交换后的结果放在[result,result+(last-first))区间中。另一个将迭代器区间[first1,last1)的元素i,依次与[first2,first2+(last-first))的元素j,执行二元函数操作binary_op(i,j),交换结果放在[result,result+(last1-first1)

//下面是函数式编程里面的map,因为c++的for_each不允许对象被修改,所以这里使用transform 
//map操作表示对容器中每个元素都执行相同的操作 
template <typename Collection,typename unop>
Collection map(Collection col,unop op) 
{
	std::transform(col.begin(),col.end(),col.begin(),op);
	return col;
}
//////////////使用///////////////
  // 用map来对vector中每个元素+1 
  println("running map operation");
  auto addOne = [] (int i) { return i+1;};
  auto returnCol = map(col,addOne);
  for_each(returnCol,lambda_echo);

reduce

改变来原始的容器元素

//transform实现reduce
//这里的参数是复制过来的,所以在函数里面修改col容器,并不影响原始数据;
template <typename Collection,typename binop>
auto reduce(Collection col,binop op) 
{
	//Collection result(col.size());
	std::transform(col.begin(),col.end()-1,col.begin()+1,col.begin()+1,op);
	return *(col.end()-1);
}
//////////////使用///////////////
//这里用reduce来认为vector每一位是一个大数 ;
println("running reduce operation");
auto reduceAdd = [] (int a, int b){ return a+b;};
int bigNum = reduce(col,reduceAdd);
cout<<"reduce add result:"<<bigNum<<endl;

transform实现zip

zip函数将传进来的两个参数中相应位置上的元素组成一个pair数组。如果其中一个参数元素比较长,那么多余的参数会被删掉。

//zip操作表示对两个容器中对应元素执行操作,结果返回; 
template <typename Collection,typename binop>
Collection zip(Collection fc,Collection sc,binop op) 
{	
	//Collection result(fc.size());
	std::transform(fc.begin(),fc.end(),sc.begin(),fc.begin(),op);
	return fc;
}
//////////////使用///////////////
// 这里其实不是真正的zip函数,可以实现两个集合之间的对应元素的同操作; 
println("running zip operation");
auto zipAdd = [] (int a, int b){ return a+b;};
std::vector<int> secondCol{40,22,22,24,23,45,34};
auto zipCol = zip(col,secondCol,zipAdd);

find_if实现exists

表示判断集合中是否有某个元素符合条件;

// exists 表示判断集合中是否有某个元素符合条件; 
template <typename Collection,typename Condition>
bool exists(Collection col,Condition con) 
{
	auto exist = std::find_if(col.begin(),col.end(),con);
	return exist != col.end();
}
//////////////使用///////////////
println("runnig exists");
//prints 1 if true 0 if false
std::cout << "value 20 exist= " << exists(col, [] (int value){ return value==20;}) << std::endl;
std::cout << "value 43 exist= " << exists(col, [] (int value){ return value==43;}) << std::endl;

remove_if和erase实现filter

和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
remove_if()并不会实际移除序列[start, end)中的元素; 如果在一个容器上应用remove_if(), 容器的长度并不会改变(remove_if()不可能仅通过迭代器改变容器的属性), 所有的元素都还在容器里面. 实际做法是, remove_if()将所有应该移除的元素都移动到了容器尾部并返回一个分界的迭代器. 移除的所有元素仍然可以通过返回的迭代器访问到. 为了实际移除元素, 你必须对容器自行调用erase()以擦除需要移除的元素.

//和map()类似,filter()也接收一个函数和一个序列。
//和map()不同的是,filter()把传入的函数依次作用于每个元素,
//然后根据返回值是True还是False决定保留还是丢弃该元素。
template <typename Collection,typename Predicate>
Collection filterNot(Collection col,Predicate predicate ) 
{   
    auto returnIterator = std::remove_if(col.begin(),col.end(),predicate);
    col.erase(returnIterator,std::end(col));    
    return col;
}

template <typename Collection,typename Predicate>
Collection filter(Collection col,Predicate predicate) 
{
	 auto fnCol = filterNot(col,[predicate](typename Collection::value_type i) { return !predicate(i);});
	 return fnCol; 
}
//////////////使用///////////////
println("running filterNot");
auto filterCol = filterNot(col,[](int value){ return value==23;});
for_each(filterCol,lambda_echo);

println("running filter");
auto filteredCol = filter(col,[](int value){ return value > 30;});
for_each(filteredCol,lambda_echo); 

参考资料

是不是服务端编程刚开始都得从写业务开始? - 回答作者 函数式编程 简单的程序诠释C++ STL算法系列之十八:transform std::remove_if scala zip函数族详解 Functional programming in C++

时间工具 chrono

duration

duration 是chrono命名空间下面的一个模板类型,它有一些实例类型如下:

typedef duration<long long, nano> nanoseconds; //纳秒
typedef duration<long long, micro> microseconds;//微秒
typedef duration<long long, milli> milliseconds;//毫秒
typedef duration<long long> seconds;
typedef duration<int, ratio<60> > minutes;
typedef duration<int, ratio<3600> > hours;

当要取得一个duration实例类型的变量的值的时候,使用count成员函数

sec.count()

当想要对duration进行单位类型转换的时候,可以使用duration_cast进行强制类型转换;

chono::minutes min = duration_cast<chono::minutes>(sec)

time_point

有三种类型 steady_clock(稳定常用)
system_clock(直接读取系统时间,可能被人手动改变)
high_resolution_clock(精度更高,单在vc库里面就是system_clock())

std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
std::cout << "Hello World\n";
std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();
std::cout << "Printing took "
  << std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count()
  << "us.\n";

#include <chrono>  
#include <iostream> 
using namespace std;
using namespace chrono;

int main()
{
	auto start = system_clock::now();//这个是chrono的time_point类型; 
	// do something...
	int sum=0;
	for(int i=0;i<10000000;i++)
	{
		sum+=i;
	}
	auto end   = system_clock::now();
	auto duration = duration_cast<microseconds>(end - start);
	cout <<  "花费了" 
	     << duration.count() 
	     << "微秒" << endl;
	     
	return 0;
}

参考资料

C++11 STL 的時間函式庫:chrono C++11 新的计时方法——std::chrono 大法好

tuple

可以认为tuple是一个未命名的结构体,该结构体包含了特定的tuple元素类型的数据成员。特别需要指出的是,tuple中元素是被紧密地存储的(位于连续的内存区域),而不是链式结构。元素的类型可以不一样。tuple元组定义了一个有固定数目元素的容器,其中的每个元素类型都可以不相同,这与其他容器有着本质的区别.是对pair的泛化。
可以显式地声明tuple的元素类型,也可以通过make_tuple()来推断出元素类型。另外,可以使用get()来通过索引(和C++的数组索引一样,从0而不是从1开始)来访问tuple中的元素。

四个常用函数

make_tuple

通过make_tuple()来推断出元素类型,以此来构造一个tuple,也可以显式定义一个tuple,如果只有两个元素可以使用make_part进行赋值;tuple是对pair的泛化;

get(tuple)

通过索引来 读写 tuple中的元素

tie()

tie函数用在式子左边,可以将变量连接到一个给定的tuple上,可以通过tie()函数的使用方便的对tuple进行“解包”操作。解包时,我们如果只想解某个位置的值时,可以用std::ignore占位符来表示不解某个位置的值。 tie函数用在式子右边,它会创建一个元组的左值引用.

tuple_cat()函数

通过该函数可以将多个tuple连接起来形成一个tuple

关于tuple的遍历,元素个数的获取,见我的代码 cpp11_19_tuple.cpp

参考资料

c++11FAQ】标准库中的元组(std::tuple)
C++11 tuple——KingsLanding
C++11改进我们的程序之简化我们的程序(七)tuple
介绍C++11标准的变长参数模板
泛化之美--C++11可变模版参数的妙用

线程

C++11提供了新头文件 <thread>、<mutex>、<atomic>、<future>等用于支持多线程。

async

async()函数是一个简单任务的”启动”(launcher)函数。 代码见:cpp11_14_async.cpp