Boost库编程相关详解笔记
历史 1998年,Deman G.Dawes(C++标准委员会成员之一)发起倡议并建立了Boost社区
目的是向C++程序员提供免费、同行审查、可移植的高质量C++源程序库
Bost发布采用Boost Software License不同于GPL,Apache的非常宽松许可证,允许用户将boost用于任何用途,既鼓励商业用途,也鼓励非商业用途,用户无须之父任何费用,不收任何限制享用Boost全部功能
结构 1 2 3 4 5 6 7 8 9 10 11 12 accumlators //累加器 algorithm //算法库 align //内存对齐库 archive //序列化库 asio //异步并法库 assign //赋值初始化库 atomic //院子操作库 bitmap //双向关联数组 bind //bind表达式 chrono //时间处理库 circular_buffer//循环缓冲区容器 xpressive //正则表达式库
链接方式 Boost大部分C++类的声明和实现都放在一个文件,而不分成两个文件,也就是”.h+.cpp”,故文件后缀是”.hpp”
剩下少量库都是要编译成静态库或者动态库
chrono
date_time
regex
program_options
test
thread
python等
时间日期 timer库 提供简易的时间和进度显示
timer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> using namespace std ;#include <boost/timer.hpp> using namespace boost;int main () { timer t; cout << CLOCKS_PER_SEC << endl ; cout << "max timespan:" << t.elapsed_max() /3600 << "h" <<endl ; cout << "min timespan:" << t.elapsed_min() << "s" << endl ; cout << "now time elapsed:" << t.elapsed() <<"s" << endl ; }
使用了标注库头<ctime>
的std::lock()
函数,返回自进程启动以来的clock
数,每秒的clock
数由宏定义CLOCKS_PER_SEC
定义
构造函数记录当前clock数作计时起点,保存私有成员变量_start_time
_elapsed获取此时的clock数减去计时起点_start_time 再除以CLOCKS_PER_SEC获得以秒为单位的已经流失时间
restart()重置开始计时
elapsed_min()返回timer能够测量的最小时间单位,是CLOCKS_PER_SEC的倒数
elapsed_max()使用数值极限类numeric_limits,获得clock_t类型的最大值,采用类似elapsed()方式计算可能的最大时间范围
progress_timer 但是其有一个析构函数,析构的时候会自动调用elapsed()输出从构造到析构所消耗的时间,所以利用这个类来测时间是非常方便的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <boost/progress.hpp> using namespace boost;int main () { { boost::progress_timer t; } { boost::progress_timer t; } stringstream ss; { progress_timer t (ss) ; } cout << ss.str(); }
progress_display 独立的类,与timer库其他两个组件timer
&progress_timer
没有任何联系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <fstream> #include <iostream> #include <string> #include <vector> using namespace std ;#include <boost/progress.hpp> using namespace boost;void case1 () { vector <string > v(100 ); ofstream fs ("./test.txt" ) ; progress_display pd(v.size()); for (auto & x : v) { fs << x << endl ; ++pd; } } void case2 () { vector <string > v(100 , "aaa" ); v[10 ] = "" ;v[23 ] = "" ; ofstream fs ("./test.txt" ) ; progress_display pd(v.size()); for (auto pos = v.begin(); pos != v.end();++pos) { fs << *pos << endl ; ++pd; if (pos->empty()) { cout << "null string # " << (pos - v.begin())<< endl ; } } } int main () { case1(); case2(); }
progress_display
构造函数接收一个long类型参数,作为进度显示的基数
另一种形式构造函数还接收(基数,std::cout类的输出流,进度描述,框描述,数值描述)
1 2 3 4 5 progress_display pd(v.size(),cout ,"%%%", "+++", "???");
注意事项
boost的process_display采用了标准库的输出,不要和其他程序处理std::cout一起输出,否则会乱
date_time库 日期 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> using namespace std ;#include <boost/date_time/gregorian/gregorian.hpp> using namespace boost::gregorian;void case1 () { date d1; date d2 (2010 ,1 ,1 ) ; date d3 (2000 , Jan , 1 ) ; date d4 (d2) ; assert(d1 == date(not_a_date_time)); assert(d2 == d4); assert(d3 < d4); }
day_lock
天级别的始终,工厂类调用了静态成员函数local_day
或universal_day
返回一个当天的日期对象,分别表示本地日期和UTC日期,day_lock
内部使用了C标准库的localtime
和gmttime
函数,因此local_day
依赖操作系统时区设置
1 2 3 4 5 6 7 8 9 10 11 12 void case2 () { date d1 = from_string("1999-12-31" ); date d2 ( from_string("2015/1/1") ); date d3 = from_undelimited_string("20011118" ) ; cout << d1 << d2 << d3 << endl ; cout << day_clock::local_day() << endl ; cout << day_clock::universal_day() << endl ; }
使用special_values
枚举来创建特殊的日期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void case3 () { date d1 (neg_infin) ; date d2 (pos_infin) ; date d3 (not_a_date_time) ; date d4 (max_date_time) ; date d5 (min_date_time) ; cout << d1 << d2 << d3 << d4 << d5 << endl ; try { date d3 (2017 ,2 ,29 ) ; } catch (std ::exception& e) { cout << e.what() << endl ; } }
访日日期 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void case4 () { date d (2017 ,6 ,1 ) ; assert(d.year() == 2017 ); assert(d.month() == 6 ); assert(d.day() == 1 ); date::ymd_type ymd = d.year_month_day(); assert(ymd.year == 2017 ); assert(ymd.month == 6 ); assert(ymd.day == 1 ); cout << d.day_of_week() << endl ; cout << d.day_of_year() << endl ; assert(d.end_of_month() == date(2017 ,6 ,30 )); cout << date(2015 ,1 ,10 ).week_number() << endl ; cout << date(2016 ,1 ,10 ).week_number() << endl ; cout << date(2017 ,1 ,10 ).week_number() << endl ; assert(date(pos_infin).is_infinity() ); assert(date(pos_infin).is_pos_infinity() ); assert(date(neg_infin).is_neg_infinity() ); assert(date(not_a_date_time).is_not_a_date() ); assert(date(not_a_date_time).is_special() ); assert(!date(2017 ,5 ,31 ).is_special() ); }
日期的输出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void case5 () { date d (2017 ,1 ,23 ) ; cout << to_simple_string(d) << endl ; cout << to_iso_string(d) << endl ; cout << to_iso_extended_string(d) << endl ; cout << d << endl ; }
日期长度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void case7 () { days dd1(10), dd2(-100), dd3(255); //各个天数 assert( dd1 > dd2 && dd1 < dd3); assert( dd1 + dd2 == days(-90 )); assert((dd1 + dd3).days() == 265 ); assert( dd3 / 5 == days(51 )); weeks w (3 ) ; assert(w.days() == 21 ); months m (5 ) ; years y (2 ) ; months m2 = y + m; assert(m2.number_of_months() == 29 ); assert((y * 2 ).number_of_years() == 4 ); }
日期运算 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 void case8 () { date d1(2000,1,1),d2(2017,11,18); cout << d2 - d1 << endl ; assert(d1 + (d2 - d1) == d2); d1 += days(10 ); assert(d1.day() == 11 ); d1 += months(2 ); assert(d1.month() == 3 && d1.day() == 11 ); d1 -= weeks(1 ); assert(d1.day() == 4 ); d2 -= years(10 ); assert(d2.year() == d1.year() + 7 ); { date d1 (2017 ,1 ,1 ) ; date d2 = d1 + days(pos_infin); assert(d2.is_pos_infinity()); d2 = d1 + days(not_a_date_time); assert(d2.is_not_a_date()); d2 = date(neg_infin); days dd = d1 - d2; assert(dd.is_special() && !dd.is_negative()); } { date d (2017 ,3 ,30 ) ; d -= months(1 ); d -= months(1 ); d += months(2 ); assert(d.day() == 31 ); } }
日期区间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <boost/date_time/gregorian/gregorian.hpp> using namespace boost::gregorian;void case1 () { date_period dp(date(2017,1,1), days(20)); dp.shift(days(3 )); assert(dp.begin().day() == 4 ); assert(dp.length().days() == 20 ); dp.expand(days(3 )); assert(dp.begin().day() == 1 ); assert(dp.length().days() == 26 ); }
date_period
还可以使用成员函数判断某个日期是否在区间,或者计算日期区间的交集
is_before()/is_after();
日期区间是否在日期前或后
contains();
日期区间是否包含另一个区间或者日期
intersects()
两个日期区间是否存在交集
intersection()
返回两个区间的交集,如果无交集返回一个无效区间
is_adjacent()
两个日期区间是否相邻
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void case2 () { date_period dp(date(2010,1,1), days(20)); assert(dp.is_after(date(2009 ,12 ,1 ))); assert(dp.is_before(date(2010 ,2 ,1 ))); assert(dp.contains(date(2010 ,1 ,10 ))); date_period dp2(date(2010,1,5), days(10)); assert(dp.contains(dp2)); assert(dp.intersects(dp2)); assert(dp.intersection(dp2) == dp2); date_period dp3(date(2010,1,21), days(5)); assert(!dp3.intersects(dp2)); assert(dp3.intersection(dp2).is_null()); assert(dp.is_adjacent(dp3)); assert(!dp.intersects(dp3)); }
merge()
返回两个区间的并集,如果区间无交集或不相邻返回无效区间
span()
合并两日期区间及两者间的间隔,
1 2 3 4 5 6 7 8 9 10 11 12 void case3 () { date_period dp1(date(2010,1,1), days(20)); date_period dp2(date(2010,1,5), days(10)); date_period dp3(date(2010,2,1), days(5)); date_period dp4(date(2010,1,15), days(10)); assert( dp1.contains(dp2) && dp1.merge(dp2) == dp1); assert(!dp1.intersects(dp3) && dp1.merge(dp3).is_null()); assert( dp1.intersects(dp2) && dp1.merge(dp4).end() == dp4.end()); assert( dp1.span(dp3).end() == dp3.end()); }
日期迭代器
day_iterator
:天迭代器
week_iterator
:周迭代器
month_iterator
:月迭代器
year_iterator
:年迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void case4 () { date d (2007 ,9 ,28 ) ; day_iterator d_iter (d) ; assert(d_iter == d); ++d_iter; assert(d_iter == date(2007 ,9 ,29 )); year_iterator y_iter (*d_iter, 10 ) ; assert(y_iter == d + days(1 )); ++y_iter; assert(y_iter->year() == 2017 ); day_iterator iter(day_clock::local_day()); ++iter; }
时间 操作时间长度 time_duration
构造函数指定时分秒和微妙来构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <boost/date_time/gregorian/gregorian.hpp> using namespace boost::gregorian;#include <boost/date_time/posix_time/posix_time.hpp> using namespace boost::posix_time;void case1 () { { time_duration td = duration_from_string("1:10:30:001" ); cout << td << endl ; time_duration td1 (1 ,10 ,30 ,1000 ) ; time_duration td2 (1 ,60 ,60 ,1000 *1000 * 6 + 1000 ) ; } hours h (1 ) ; minutes m (10 ) ; seconds s (30 ) ; millisec ms (1 ) ; time_duration td = h + m + s + ms; time_duration td2 = hours(2 ) + seconds(10 ); cout << td << td2 << endl ; }
时间长度精确度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void case2 () { time_duration td (1 ,10 ,30 ,1000 ) ; assert(td.hours() == 1 && td.minutes() == 10 && td.seconds() == 30 ); assert(td.total_seconds() == 1 *3600 + 10 *60 + 30 ); assert(td.total_milliseconds() == td.total_seconds()*1000 + 1 ); assert(td.fractional_seconds() == 1000 ); hours h (-10 ) ; assert(h.is_negative()); time_duration h2 = h.invert_sign(); assert(!h2.is_negative() && h2.hours() == 10 ); time_duration td1 (not_a_date_time) ; assert(td1.is_special() && td1.is_not_a_date_time()); time_duration td2 (neg_infin) ; assert(td2.is_negative() && td2.is_neg_infinity()); }
创建时间点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void case5 () { ptime p(date(2017,7,7), hours(1)); ptime p1 = time_from_string("2017-7-7 01:00:00" ); ptime p2 = from_iso_string("20170707T010000" ); cout << p1 << endl << p2; { ptime p1 = second_clock::local_time(); ptime p2 = microsec_clock::universal_time(); cout << p1 << endl << p2; } }
操作时间点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void case6 () { ptime p(date(2010,3,20), hours(12)+minutes(30)); date d = p.date(); time_duration td = p.time_of_day(); assert(d.month() == 3 && d.day() == 20 ); assert(td.total_seconds() == 12 *3600 + 30 *60 ); ptime p1(date(2010,3,20), hours(12)+minutes(30)); ptime p2 = p1 + hours(3 ); assert(p1 < p2); assert(p2 - p1 == hours(3 )); p2 += months(1 ); assert(p2.date().month() == 4 ); cout << endl ; { ptime p(date(2017,2,14), hours(20)); cout << to_simple_string(p) << endl ; cout << to_iso_string(p) << endl ; cout << to_iso_extended_string(p) << endl ; } }
时间迭代器 1 2 3 4 5 6 7 8 9 10 void case9 () { ptime p(date(2017,5,31),hours(10)) ;//10个小时的步长 for (time_iterator t_iter(p, minutes(10 )); t_iter < p + hours(1 ); ++ t_iter) { cout << *t_iter << endl ; } }
内存管理 智能指针简介 程序员如果保证new和delete保证对应,四处编写异常捕获代码以释放资源
而智能指针则可以在退出作用于:(不管正常流程离开或是异常离开),总调用delete来析构在堆上动态分配的对象
最著名是C++98标准中的”自动指针”,std::auto_ptr
,它部分地解决了获取资源自动释放的问题
但是std::auto_ptr
已经在C++11标准被声明废弃,现在应该使用最新的智能指针std::unique_ptr
,std::shared_ptr
,std::weak_ptr
而boost.smart_ptr提供了六种智能指针
scoped_ptr
scoped_array
shared_ptr
shared_array
weak_ptr
intrusive_ptr
scoped_ptr 只在作用域内生效,离开作用域既释放资源,不能复制和赋值。类似于标准库的auto_ptr,但它相对于auto_ptr的优势在于,他的要求更严格,使用起来更安全。auto_ptr拥有转移语义,当使用了赋值和复制操作时可能操作未定义行为。
具体结构
构造函数:接收类型T*的指针p,创建scoped_ptr对象,内部保存指针参数p
析构函数:使用delete操作符自动销毁所保存的指针对象,从而正确回收资源
拷贝构造函数:声明为私有,不允许对智能指针拷贝操作
赋值操作符:声明为私有,不允许对智能指针拷贝操作
reset函数:重置,删除原指针,保存新指针值
operator*()和operator ->():重置操作符*和箭头,模仿被代理的原指针
swap:高效操作,交换两个scoped_ptr的内部原始指针
bool语境:测试是否持有一个有效指针
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <boost/smart_ptr.hpp> #include <boost/smart_ptr/make_unique.hpp> using namespace boost;void case1 () { scoped_ptr<string > sp(new string ("text" )); assert(sp); assert(sp != nullptr ); cout << *sp << endl ; cout << sp->size() << endl ; }
不需要delete,scoped_ptr自动帮助释放资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 struct posix_file { posix_file(const char * file_name) { cout << "open file:" << file_name << endl ; } ~posix_file() { cout << "close file" << endl ; } }; void case2 () { scoped_ptr<posix_file> fp(new posix_file("/tmp/a.txt" )); scoped_ptr<int > p(new int ); if (p) { *p = 100 ; cout << *p << endl ; } p.reset(); assert(p == 0 ); if (!p) { cout << "scoped_ptr == nullptr" << endl ; } }
unique_ptr区别 std::unique_ptr
是c++11定义的新智能指针,用来取代c++98中的std::auto_ptr
std::unique_ptr
不仅能代理new创建的单个对象还能代理new[]创建的数组对象 也就是它结合了scoped_array
和scoped_ptr
两者的能力
std::unique_ptr
基本能力和scoped_ptr
相同,同样作用于管理指针,也不允许拷贝构造和拷贝赋值
但std::unique_ptr
比scoped_ptr
功能要多,可以像原始指针一样进行比较,可以像shared_ptr
一样定制删除器,也可以安全地放入标准容器
make_unique
boost提供了unique_ptr的工厂类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void case_unique () { auto p = boost::make_unique<int >(10 ); assert(p && *p == 10 ); p.release(); assert(!p); auto a = boost::make_unique<int []>(5 ); a[0 ] = 100 ; a[4 ] = 500 ; }
scoped_array
用法 scoped_array与scoped_ptr源于相同设计思想,用法相似,但是scoped_array包装的是new[]产生的指针 并在析构的时候delete[]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> using namespace std ;#include <boost/smart_ptr.hpp> using namespace boost;void case1 () { int *arr = new int [100 ]; scoped_array<int > sa(arr); fill_n(&sa[0 ],100 , 5 ); sa[10 ] = sa[20 ] + sa[30 ]; }
unique_ptr区别 unique_ptr的数组对象用法和scoped_array基本相同,但模板参数中需要声明为数组类型
1 2 3 4 5 6 7 8 9 10 11 void case2 () { unique_ptr <int []> up(new int [10 ]); assert(up); up[0 ] = 10 ; cout << up[0 ] << endl ; up.reset(); assert(!up); }
shared_ptr shared_ptr最像指针的智能指针,boost.smart_ptr库中最有价值最重要的组成部分,也是最有用
shared_ptr与scoped_ptr功能相似 都重载了*
和->操作符模仿原始指针的行为,提供了显式bool类型转换以判断指针的有效性 get()可以得到原始指针,并且没有提供指针算术操作,也不能管理new[]产生的数组动态指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> #include <exception> #include <boost/smart_ptr.hpp> using namespace boost;void case1 () { shared_ptr <int > spi(new int ); assert(spi); *spi = 253 ; shared_ptr <std ::string > sps(new std ::string ("smart" )); assert(sps->size() == 5 ); }
reset也只是引用计数减1,除非引用计数为0,否则不会发生删除操作
运用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 void case2 () { typedef shared_ptr <std ::string > sp_t ; std ::map <sp_t , int > m; sp_t sp(new std ::string ("one" )); m[sp] = 111 ; shared_ptr <std ::exception> sp1(new std ::bad_exception()); auto sp2 = dynamic_pointer_cast<std ::bad_exception>(sp1); auto sp3 = static_pointer_cast<std ::exception>(sp2); assert(sp3 == sp1); } void case3 () { shared_ptr <int > sp(new int (10 )); assert(sp.unique()); shared_ptr <int > sp2 = sp; assert(sp == sp2 && sp.use_count() == 2 ); *sp2 = 100 ; assert(*sp == 100 ); sp.reset(); assert(!sp); } class shared { private : shared_ptr <int > p; public : shared(shared_ptr <int > p_):p(p_){} void print () { std ::cout << "count:" << p.use_count() << " v=" <<*p << std ::endl ; } }; void print_func (shared_ptr <int > p) { std ::cout << "count:" << p.use_count() << " v=" <<*p << std ::endl ; } void case4 () { shared_ptr <int > p(new int (100 )); shared s1(p), s2(p); s1.print(); s2.print(); *p = 20 ; print_func(p); s1.print(); }
工厂函数 为了去除过多显式的new草祖父,应该使用工厂模式来解决
1 2 3 4 5 6 void case5 () { auto sp = make_shared<std ::string >("make_shared" ); auto spv = make_shared<std ::vector <int >>(10 , 2 ); assert(spv->size() == 10 ); }
应用于标准容器
shared_ptr<list> 使得容器安全共享
vector<shared_ptr> 因为shared_ptr支持拷贝语义和比较操作,符合标准容器对元素的要求,可以安全容纳元素的指针而不是拷贝
但是容器不能容纳scoped_ptr,因为scoped_ptr不能拷贝和复制,标准容器可以容纳原始指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 typedef std ::vector <shared_ptr <int > > vs; vs v (10 ) ; int i = 0 ; for (auto pos = v.begin(); pos != v.end(); ++pos) { (*pos) = make_shared<int >(++i); std ::cout << *(*pos) << ", " ; } std ::cout << std ::endl ; for (auto & ptr : v) { ptr = make_shared<int >(++i); std ::cout << *ptr << ", " ; } std ::cout << std ::endl ; shared_ptr <int > p = v[9 ]; *p = 100 ; std ::cout << *v[9 ] << std ::endl ;
应用于桥接模式 此模式具体实现细节对用户隐藏,达到类的最小耦合关系
scoped_ptr和shared_ptr都可以实现桥接模式,shared_ptr更适合
因为它支持拷贝和复制,可以配合容器工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <boost/core/ignore_unused.hpp> #include <boost/smart_ptr.hpp> using namespace boost;class sample { private : class impl ; shared_ptr <impl> p; public : sample(); void print () ; }; class sample : :impl { public : void print () { std ::cout << "impl print" << std ::endl ;}}; sample::sample():p(new impl){} void sample::print(){ p->print();} void case1 () { sample s; s.print(); }
应用于工厂模式 工厂模式创造型设计模式
包装了new操作符的使用,使得对象的创建工作集中在工厂类或者工厂函数
make_shared是工厂模式的一个好例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class abstract { public : virtual void f () = 0 ; virtual void g () = 0 ; protected : virtual ~abstract() = default ; }; class impl :public abstract{ public : impl() = default ; virtual ~impl() = default ; public : virtual void f () { std ::cout << "class impl f" << std ::endl ; } virtual void g () { std ::cout << "class impl g" << std ::endl ; }}; shared_ptr <abstract> create(){ return make_shared<impl>();} void case2 () { auto p = create(); p->f(); p->g(); abstract *q = p.get(); boost::ignore_unused(q); }
删除容器 shared_ptr第一个参数是要被管理的指针,含义和其他构造函数参数相同, 第二个删除器参数d告诉析构的时候不是使用delete来操作指针p,而是用d来操作 delete p 换成了d(p)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class socket_t { };socket_t * open_socket() { std ::cout << "open_socket" << std ::endl ; return new socket_t ; } void close_socket (socket_t * s) { std ::cout << "close_socket" << std ::endl ; } void case3 () { socket_t *s = open_socket(); shared_ptr <socket_t > p(s, close_socket); }
删除容器的高级用法
这样函数栈结束会自动执行这个方法
1 2 3 4 5 6 7 void any_func (void * p) { std ::cout << "some operate" << std ::endl ;}void case5 () { shared_ptr <void > p(nullptr ,any_func); }
别名构造函数 shared_ptr还有一个特殊的构造函数 如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 template <class Y >shared_ptr <shared_ptr<Y> const & r, element_type *p);void case6() { auto p1 = make_shared<std::pair<int, int>>(0,1); shared_ptr<int> p2(p1, &p1->second ); assert(p1.use_count() == 2 && p1.use_count() == p2.use_count()); assert((void *)p1.get() != (void *)p2.get()); assert(&p1->second== p2.get()); }
shared_array
应用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> using namespace std ;#include <boost/smart_ptr.hpp> using namespace boost;int main () { int *p = new int [100 ]; shared_array<int > sa(p); assert(sa.unique()); shared_array<int > sa2 = sa; assert(sa2.use_count() == 2 ); sa[0 ] = 10 ; assert(sa2[0 ] == 10 ); }
operator[]要小心,不提供数组索引范围检查
weak_ptr
用法 weak_ptr的设计为与shared_ptr协同工作,可以从一个shared_ptr或另一个weak_ptr对象构造
获得资源的观测全,但它的构造不会引起指针引用计数的增加,同样weak_ptr析构也不会导致引用计数的减少,只是一个观察者
weak_ptr没有重载operator*
和->,这是特意的,因为它不共享指针,不能操作资源,
从而非常重要的成员函数就是lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> #include <boost/smart_ptr.hpp> using namespace boost;void case1 () { shared_ptr <int > sp(new int (10 )); assert(sp.use_count() == 1 ); weak_ptr<int > wp(sp); assert(wp.use_count() == 1 ); if (!wp.expired()) { shared_ptr <int > sp2 = wp.lock(); *sp2 = 100 ; assert(wp.use_count() == 2 ); } assert(wp.use_count() == 1 ); sp.reset(); assert(wp.expired()); assert(!wp.lock()); }
enable_shared_from_this weak_ptr一个重要用途是获得this指针的shared_ptr,使得自己能够生产shared_ptr管理自己
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class self_shared : public enable_shared_from_this<self_shared> { public : self_shared(int n):x(n){} int x; void print () { std ::cout << "self_shared:" << x << std ::endl ; }}; void case2 () { auto sp = make_shared<self_shared>(313 ); sp->print(); auto p = sp->shared_from_this(); p->x = 1000 ; p->print(); }
enable_shared_from_raw
enable_shared_from_raw同样需要继承使用,但他不是模板类,所以不需要指定模板参数 不提供成员函数shared_from_this(),而是用两个friend函数,shared_from_raw()和weak_from_raw()完成智能指针工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <boost/smart_ptr/enable_shared_from_raw.hpp> class raw_shared : public enable_shared_from_raw { public : raw_shared() { std ::cout << "raw_shared ctor" << std ::endl ; } ~raw_shared() { std ::cout << "raw_shared dtor" << std ::endl ; } }; void case4 () { raw_shared x; assert(weak_from_raw(&x).use_count() == 1 ); auto px = shared_from_raw(&x); assert(px.use_count() == 2 ); auto p = new raw_shared; auto wp = weak_from_raw(p); assert(wp.use_count() == 1 ); /此时无引用 0 decltype (shared_from_raw(p)) spx(p); auto sp = shared_from_raw(p); assert(sp.use_count() == 2 ); auto sp2 = sp; auto wp2 = weak_from_raw(p); assert(wp2.use_count() == 3 ); }
打破循环引用 两个节点互相持有对方引用,每个shared_ptr的引用计数都是2 因此析构引用计数没有减至0,不会调用删除操作,导致内存泄漏 这个时候可以用weak_ptr,因他不增加智能指针的引用计数,在真需要的shared_ptr的时候调用weak_ptr的lock函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class node { public : ~node() { std ::cout << "deleted" << std ::endl ;} typedef weak_ptr<node> ptr_type; ptr_type next; }; void case3 () { auto p1 = make_shared<node>(); auto p2 = make_shared<node>(); p1->next = p2; p2->next = p1; assert(p1.use_count() == 1 ); assert(p2.use_count() == 1 ); if (!p1->next.expired()) { auto p3 = p1->next.lock(); } }
intrusive_ptr intrusive也是引用计数指针,所以他的接口和shared_ptr很像 支持static_pointer_cast、dynamic_pointer_cast转型操作
它还支持
intrusive_ptr_add_ref增加引用计数
intrusive_ptr_release减少引用计数
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #include <boost/smart_ptr.hpp> using namespace boost;struct counted_data { int m_count = 0 ; ~counted_data() { cout << "dtor" << endl ; } }; void intrusive_ptr_add_ref (counted_data* p) { ++p->m_count; } void intrusive_ptr_release (counted_data* p) { if (--p->m_count == 0 ) { delete p; } } struct counted_data2 : public intrusive_ref_counter<counted_data2>{ ~counted_data2() { cout << "dtor2" << endl ; } }; int main () { typedef intrusive_ptr<counted_data> counted_ptr; counted_ptr p (new counted_data) ; assert(p); assert(p->m_count == 1 ); counted_ptr p2 (p) ; assert(p->m_count == 2 ); counted_ptr weak_p(p.get(), false); //弱引用 assert(weak_p->m_count == 2 ); p2.reset(); assert(!p2); assert(p->m_count == 1 ); { typedef intrusive_ptr<counted_data2> counted_ptr; counted_ptr p (new counted_data2) ; assert(p); assert(p->use_count() == 1 ); } }
pool库 内存池,boost.pool库基于简单分割存储思想实现了快速、紧凑的内存池库 不仅能够管理大量的对象,还可以用作stl的内存分配器, 它近似于一个小型垃圾回收机制,在大量的分配和释放小对象时非常有效率,而且完全不考虑delete
pool库包含四个部分
简单的pool
object_pool,分配类实例
singleton_pool,单例内存池
pool_alloc,标准库
pool
pool构造函数接收一个整数表示每次pool分配内存块大小,非内存池大小 pool会根据需要自动地向系统申请或归还使用的内存,在析构的时候自动释放他所持有的所有内存块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <boost/core/ignore_unused.hpp> #include <boost/pool/pool.hpp> using namespace boost;void case1 () { pool<> pl(sizeof (int )); int *p = static_cast <int *>(pl.malloc ()); assert(pl.is_from(p)); pl.free (p); for (int i = 0 ;i < 100 ; ++i) { pl.ordered_malloc(10 ); } }
但是它只适合普通数据类型int,double等内存池,不能应用复杂的类和对象
object_pool 功能和pool类似,但析构时对所有已经分配内存块调用析构函数,从而正确地释放资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <boost/pool/object_pool.hpp> struct demo_class { public : int a,b,c; demo_class(int x = 1 , int y = 2 , int z = 3 ): a(x),b(y),c(z){} }; void case2 () { object_pool<demo_class> pl; auto p = pl.malloc (); assert(pl.is_from(p)); assert(p->a!=1 || p->b != 2 || p->c !=3 ); p = pl.construct(7 , 8 , 9 ); assert(p->a == 7 ); object_pool<string > pls; for (int i = 0 ; i < 10 ; ++i) { string *ps = pls.construct("hello object_pool" ); cout << *ps << endl ; } }
singleton_pool
1 2 3 4 5 6 7 8 9 10 11 12 #define BOOST_POOL_NO_MT #include <boost/pool/singleton_pool.hpp> struct pool_tag { }; typedef singleton_pool<pool_tag, sizeof (int )> spl; void case4 () { int *p = (int *)spl::malloc (); assert(spl::is_from(p)); spl::release_memory(); }
pool_alloc 标准容器模板参数的内存分配器
字符串 lexical_cast lexical_cast库进行了字面值的转换,类似C中的atoi函数,可以进行字符串和整数/浮点数之间的字面转换
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void case1 () { int x = lexical_cast<int >("100" ); long y = lexical_cast<long >("2000" ); float pai = lexical_cast<float >("3.14159e5" ); double e = lexical_cast<double >("2.71828" ); double r = lexical_cast<double >("1.414,xyz" , 5 ); cout << x << y << pai << e << r << endl ; string str = lexical_cast<string >(456 ); cout << str << endl ; cout << lexical_cast<string >(0.618 ) << endl ; cout << lexical_cast<string >(0x10 ) << endl ; cout << lexical_cast<bool >("1" ) << endl ; }
异常处理 lexcical_cast 无法执行转换操作时抛出异常bad_lexical_cast,是std::bad_cast的派生类
1 2 3 4 5 6 7 8 9 10 try { lexical_cast<T>(str); return true ; } catch (bad_lexical_cast& e){ cout << "error:" << e.what() << endl ; return false ; };
lexical_cast在名字空间boost::conversion提供了
try_lexical_convert()函数,可以避免抛出异常,以bool返回值表示是否转换成功
1 2 3 4 5 6 template <typename T>bool num_valid (const char *str) { T tmp; return conversion::try_lexical_convert(str, tmp); }
对转换对象的要求
c++内建类型int,double等和std::string都满足以上条件
对比c++11标准 c++11标准增强了字符串与数字的互操作性,提供了
实现了std::string与数字之间的转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void case4 () try { assert(stoi(" 42 " ) == 42 ); assert(stol("100L" ) == 100L ); assert(stol("1000 9" ) == 1000L ); assert(stod("3.14ispai" ) == 3.14 ); assert(to_string(776u l) == "776" ); cout << stoi("9999999999" ); } catch (std ::exception& e){ cout << "error:" << e.what() << endl ; }
c++标准库输入输出操作符<<,>>不是完美无效,精确输出格式控制需要些大量的操作函数,而且会改变流的状态,用完还需及时回复,有时候显得十分烦琐
因此很多程序员怀念C语言中经典的printf函数,但是缺少了类型安全检查
boost.format库,摒弃了printf,实现了类似于printf()的格式化对象,可以把参数格式化到一个字符串,而且完全类型安全
1 2 3 4 5 6 7 8 9 10 11 12 void case1 () { cout << format("%s:%d+%d=%d\n" ) %"sum" % 1 % 2 % (1 +2 ); format fmt ("(%1% + %2%) * %2% = %3%\n" ) ; fmt % 2 % 5 ; fmt % ((2 +5 )*5 ); cout << fmt.str(); }
通过操作符% 逐个”喂”给format对象,完成对参数的格式化
类摘要 format不是一个真正的类,而是一个typedef,真正的实现是basic_format
str,返回已经格式化好的字符串
size,获得已经格式化好的字符串长度
parse,清空format对象内部的缓存,并运用新的字符串
clear,则是清空缓存
格式化语法
1 2 3 4 5 6 7 8 9 10 11 void case2 () { format fmt ("%05d\n%-8.3f\n% 10s\n%05X\n" ) ; cout << fmt %62 % 2.236 % "123456789" % 48 ; format fmt2 ("%|05d|\n%|-8.3f|\n%| 10s|\n%|05X|\n" ) ; cout << fmt2 %62 % 2.236 % "123456789" % 48 ; const format fmt3 ("%10d %020.8f %010X %10.5e\n" ) ; cout << format(fmt3) %62 % 2.236 % 255 % 0.618 ; }
1 2 3 4 5 6 7 8 9 10 //输出值 00062 2.236 123456789 00030 00062 2.236 123456789 00030 62 00000000002.23600000 00000000FF 6.18000e-01
高级用法 format提供了类似printf的功能,但并等同于printf函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iomanip> using namespace boost;using boost::io::group;void case3 () { format fmt ("%1% %2% %3% %2% %1% \n" ) ; cout << fmt %1 % 2 % 3 ; fmt.bind_arg(2 , 10 ); cout << fmt %1 %3 ; fmt.clear(); cout << fmt % group(showbase,oct, 111 ) % 333 ; fmt.clear_binds(); fmt.modify_item(1 , group(hex, right, showbase,setw(8 ), setfill('*' ))); cout << fmt % 49 % 20 % 100 ; }
string_ref std::string经常要拷贝来拷贝去,内存开销大,影响程序效率
使用const std::string &
可以避免一些问题,但是处理C字符串、提取子字符串又无能为力
boost.string_ref就是这样的一种轻量级字符串,它只支持字符串的引用,没有内存拷贝的成本,运行效率很高
原理简单粗暴,只用两个成员变量ptr_和len_标记字符串的起始位置和长度
remove_prefix(6); 删除前6个字符
remove_suffix(5); 删除后5个字符
……
basic_string_ref 也定了几个typedef方便使用
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <cstring> #include <iostream> #include <assert.h> using namespace std ;#include <boost/core/ignore_unused.hpp> #include <boost/utility/string_ref.hpp> using namespace boost;void case1 () { const char * ch = "Days of Future Past" ; string str (ch) ; string_ref s1 (ch) ; string_ref s2 (str) ; assert(s1 == s2 && s1 == ch && s2 == str); string_ref s3 (ch, 4 ) ; assert(s3 == str.substr(0 , 4 )); string_ref s4, s5; s4 = ch; s5 = str; assert(s4 == s5); string_ref str ("Apple iPhone iPad" ) ; str.remove_prefix(6 ); assert(str.starts_with("iP" )); str.remove_suffix(5 ); assert(str.ends_with("one" )); }
string_algo 字符串标准类std::string有一些成员函数可以查找子串,访问字符,可以执行基本的字符串处理能力
string_algo库非常全面的字符串算法库,提供了大量的字符串操作函数
大小写转换
1 2 3 4 5 string str ("FireEmblem Heroes\n" ) ;cout << to_upper_copy(str); cout << str;to_lower(str); cout << str;
判断式(算法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void case3 () { string str ("Power Bomb" ) ; assert(iends_with(str, "bomb" )); assert(!ends_with(str, "bomb" )); assert(starts_with(str, "Pow" )); assert(contains(str, "er" )); string str2 = to_lower_copy(str); assert(iequals(str, str2)); string str3 ("power suit" ) ; assert(ilexicographical_compare(str, str3)); assert(all(str2.substr(0 , 5 ), is_lower())); }
判断式(函数对象) string_algo增强了标准库的equal_to和less函数对象,允许不同类型的参数进行比较
1 2 3 4 5 6 7 8 9 10 11 void case4 () { string str1("Samus"), str2("samus"); assert(!is_equal()(str1, str2)); assert( is_less()(str1, str2)); assert(!is_equal()(str1, string_ref(str2))); }
第一个括号调用了函数对象的构造函数,产生临时对象,第二个括号才是真正的函数调用操作符operator();
分类 string_algo提供了一组分类函数,用于检测一个字符是否符合某种特性,主要用于搭配其他算法
修剪 string_algo提供三个修剪算法:trim_left,trim_right和trim
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <boost/format.hpp> struct is_zero_or_one { bool operator () (char x) { return x == '0' || x == '1' ;}}; auto is01 = [](char x) { return x == '0' || x == '1' ;}; void case5 () { format fmt ("|%s|\n" ) ; string str = " samus aran " ; cout << fmt % trim_copy(str); cout << fmt % trim_left_copy(str); trim_right(str); cout << fmt % str; string str2 = "2015 Happy new Year!!!" ; cout << fmt % trim_left_copy_if(str2, is_digit()); cout << fmt % trim_right_copy_if(str2, is_punct()); cout << fmt % trim_copy_if(str2, is_punct() || is_digit() || is_space()); } |samus aran| |samus aran | | samus aran| | Happy new Year!!!| |2015 Happy new Year| |Happy new Year|
查找 string_algo的查找算法提供与std::search()类似的功能,但接口不一样
它不返回一个迭代器(查找到的位置),而使用了boost.range库的iterator_range返回查找到的整个区间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 void case6 () { format fmt ("|%s|. pos = %d\n" ) ; string str = "Long long ago, there was a king." ; iterator_range<string ::iterator> rge; rge = find_first(str, "long" ); cout << fmt % rge % (rge.begin() - str.begin()); rge = ifind_first(str, "long" ); cout << fmt % rge % (rge.begin() - str.begin()); rge = find_nth(str, "ng" , 2 ); cout << fmt % rge % (rge.begin() - str.begin()); rge = find_head(str, 4 ); cout << fmt % rge % (rge.begin() - str.begin()); rge = find_tail(str, 5 ); cout << fmt % rge % (rge.begin() - str.begin()); rge = find_first(str, "samus" ); assert(rge.empty() && !rge); } |long |. pos = 5 |Long|. pos = 0 |ng|. pos = 29 |Long|. pos = 0 |king.|. pos = 27
替换与删除 替换、删除与查找算法接近,对查找到的结果进行字符串处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void case7 () { string str = "Samus beat the monster.\n" ; cout << replace_first_copy(str, "Samus" , "samus" ); replace_last(str, "beat" , "kill" ); cout << str; replace_tail(str, 9 , "ridley.\n" ); cout << str; cout << ierase_all_copy(str, "samus" ); cout << replace_nth_copy(str, "l" , 1 , "L" ); cout << erase_tail_copy(str, 8 ); } samus beat the monster. Samus kill the monster. Samus kill the ridley. kill the ridley. Samus kilL the ridley. Samus kill the
分割 string_algo提供了两个字符串分割算法,find_all和split
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void case8 () { string str = "Samus,Link.Zelda::Mario-Luigi+zelda" ; deque <string > d; ifind_all(d, str, "zELDA" ); assert(d.size() == 2 ); for (auto x : d) { cout << "[" << x << "] " ; } cout << endl ; list <iterator_range<string ::iterator> > l; split(l, str, is_any_of(",.:-+" )); for (auto x : l) { cout << "[" << x << "]" ; } cout << endl ; l.clear(); split(l, str, is_any_of(".:-" ), token_compress_on); for (auto x : l) { cout << "[" << x << "]" ; } cout << endl ; } [Zelda] [zelda] [Samus][Link][Zelda][][Mario][Luigi][zelda] [Samus,Link][Zelda][Mario][Luigi+zelda]
合并 合并算法join是分割算法的逆运算 把容器连接成新字符串,并指定连接的分隔符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <boost/assign.hpp> void case9 () { using namespace boost::assign; vector <string > v = list_of("Samus" )("Link" )("Zelda" )("Mario" ); cout << join(v, "+" ) << endl ; cout << join_if(v, "**" , [](string_ref s) { return contains(s, "a" ); } ); cout << endl ; }
查找(分隔)迭代器 通用的find_all或split之外, string_algo库提供了两个查找迭代器find_iterator和split_iterator
可实现字符串中像迭代器那样遍历匹配,执行查找或分割,无需使用容器来容纳
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void case10 () { string str ("Samus||samus||mario||||Link" ) ; auto pos = make_find_iterator(str, first_finder("samus" , is_iequal())); decltype (pos) end; for (; pos != end; ++pos) { cout << "[" << *pos << "]" ; } cout << endl ; typedef split_iterator<string ::iterator> string_split_iterator; string_split_iterator p, endp; for (p = make_split_iterator(str, first_finder("||" , is_iequal())); p != endp; ++p) { cout << "[" << *p << "]" ; } cout << endl ; }
调用first_finder()函数,用于判断匹配对象,然后在用make_find_iterator()火make_split_iterator来真正创建迭代器
查找函数除了first_finder还有如下
last_finder
nth_finder
token_finder
数学/数字 math
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> #include <type_traits> using namespace std ;#include <boost/math/constants/constants.hpp> using namespace boost::math;void case1 () { cout << setprecision(64 ); auto a = float_constants::pi * 2 * 2 ; cout << "area \t\t= " << a << endl ; using namespace double_constants; auto x = root_two * root_three; cout << "root 2 * 3 \t= " << x << endl ; cout << "root pi \t= " << root_pi << endl ; cout << "pi pow e \t= " << pi_pow_e << endl ; } area = 12.56637096405029296875 root 2 * 3 = 2.449489742783178325424842114443890750408172607421875 root pi = 1.7724538509055161039640324815991334617137908935546875 pi pow e = 22.459157718361044686616878607310354709625244140625
高级用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <boost/multiprecision/cpp_dec_float.hpp> void case2 () { using namespace constants; typedef decltype (pi<float >) pi_t ; assert(is_function<pi_t >::value); assert(pi<float >() == float_constants::pi); assert(pi<double >() == double_constants::pi); typedef boost::multiprecision::cpp_dec_float_100 float_100; cout << setprecision(100 ) << pi<float_100>() << endl ; } 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068
高精度数组库boost::multiprecision
integer integer库提供了一组有关整数处理的头文件和类,具有良好的可移植性
integer_traits integer_traits 整数特征类,由于继承自std::numeric_limits<>
,因此拥有std::numeric_limits
的全部能力
min最小值
max最大值
const_min静态最小
const_max静态最大
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <boost/integer_traits.hpp> using namespace boost;void case1 () { cout << integer_traits<int >::const_max << endl ; cout << integer_traits<bool >::const_min << endl ; cout << integer_traits<long >::is_signed << endl ; } 2147483647 0 1
标准整数类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <boost/cstdint.hpp> #include <limits> void case2 () { uint8_t u8; int_fast16_t i16; int_least32_t i32; uintmax_t um; u8 = 255 ; i16 = 32000 ; i32 = i16; um = u8 + i16 + i32; cout << "u8 :" << sizeof (u8) << " v = " << (short )u8 << endl ; cout << "i16 :" << sizeof (i16) << " v = " << i16 << endl ; cout << "i32 :" << sizeof (i32) << " v = " << i32 << endl ; cout << "um :" << sizeof (um) << " v = " << um << endl ; cout << (short )numeric_limits<int8_t >::max() << endl ; cout << numeric_limits<uint_least16_t >::max() << endl ; cout << numeric_limits<int_fast32_t >::max() << endl ; cout << numeric_limits<intmax_t >::min() << endl ; } u8 :1 v = 255 i16 :2 v = 32000 i32 :4 v = 32000 um :8 v = 64255 127 65535 2147483647 -9223372036854775808
ratio 小时、千克、尺等待为,
random
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 #include <iostream> #include <boost/random.hpp> using namespace boost;void case1 () { mt19937 rng(time(0)); std ::cout << mt19937::min() << "<->" << mt19937::max() << std ::endl ; for (int i = 0 ;i < 100 ;++i) { std ::cout << rng() << "," ; } rng.discard(5 ); std ::vector <int > vec(10 ); rng.generate(vec.begin(), vec.end()); } void case2 () { mt19937 rng(time(0)); std ::cout << rng() << std ::endl ; mt19937 rng2 (rng) ; for (int i = 0 ;i < 10 ;++i) { assert(rng() == rng2()); } } void case3 () { mt19937 rng(time(0)); random::uniform_int_distribution<> ui(0 , 255 ); for (int i = 0 ;i < 10 ;++i) { std ::cout << ui(rng) << "," ; } assert(ui.a() == 0 && ui.b() == 255 ); std ::cout << std ::endl ; uniform_01<> u01; for (int i = 0 ;i < 10 ;++i) { std ::cout << u01(rng) << "," ; } std ::cout << std ::endl ; normal_distribution<> nd(1 , 2 ); int count = 0 ; for (int i = 0 ;i < 10000 ;++i) { if (abs (nd(rng) - 1 ) <= 2.0 ) { ++count; } } std ::cout << 1.0 * count / 10000 << std ::endl ; } void case4 () { mt19937 rng((int32_t)time(0)); uniform_smallint<> us(1 ,100 ); variate_generator<mt19937&, uniform_smallint<>> gen(rng, us); for (int i = 0 ; i < 10 ; ++i) { std ::cout << gen() << std ::endl ; } } template <typename Rng >void rand_bytes (unsigned char *buf, int buf_len) { typedef variate_generator<Rng, uniform_smallint<>> var_gen_t ; static var_gen_t gen(Rng((typename Rng::result_type)time(0 )), uniform_smallint<>(1 ,255 )); generate_n(buf, buf_len, std ::ref(gen)); } void case5 () { unsigned char buf[10 ]; rand_bytes<mt19937>(buf, 10 ); for (int i = 0 ;i < 10 ;++i) { std ::cout << (short )buf[i] << "," ; } std ::cout << std ::endl ; rand_bytes<rand48>(buf, 10 ); for (int i = 0 ;i < 10 ;++i) { std ::cout << (short )buf[i] << "," ; } std ::cout << std ::endl ; } #include <boost/nondet_random.hpp> class boost : :random_device::impl{ private : rand48 rng; public : impl():rng(time(0 )) { std ::cout << "random_device::impl ctor\n" ; } ~impl() { std ::cout << "random_device::impl dtor\n" ; } unsigned int operator () () { return rng(); }}; boost::random_device::random_device() : pimpl(new impl) {} boost::random_device::~random_device() { delete pimpl;} double boost::random_device::entropy() const { return 10 ;} unsigned int boost::random_device::operator ()(){ return (*pimpl)();} void case6 () { random_device rng; for (int i = 0 ;i < 10 ; ++i) { std ::cout << rng() << "," ; } std ::cout << std ::endl ; uniform_real<> ur(1.0 , 2.0 ); for (int i = 0 ;i < 10 ; ++i) { std ::cout << ur(rng) << "," ; } std ::cout << std ::endl ; variate_generator<random_device&, uniform_smallint<>> gen(rng, uniform_smallint<>(0 ,255 )); for (int i = 0 ;i < 10 ; ++i) { std ::cout << gen() << "," ; } std ::cout << std ::endl ; } int main () { case1(); case2(); case3(); case4(); case5(); case6(); }
算法 c++98提供大量算法,超过100个,可以对容器执行统计、查找、赋值、排序等操作
c++11、14增强了标准算法,增加了all_of、any_of、none_of等算法
foreach foreach库提供了两个宏,BOOST_FOREACH和BOOST_REVERSE_FOREACH分别实现对序列的正向遍历和反向遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <boost/foreach.hpp> #include <boost/assign.hpp> void case1 () { using namespace boost::assign; vector <int > v = (list_of(1 ),2 ,3 ,4 ,5 ); BOOST_FOREACH(auto x, v) { cout << x << "," ; } cout << endl ; string str ("boost foreach" ) ; BOOST_REVERSE_FOREACH(auto & c, str) { cout << c << "-" ; } set <int > s = list_of(10 )(20 )(30 ); int x; BOOST_FOREACH (x, s) { if (++x % 7 == 0 ) { cout << x << endl ; break ; } } } 1 ,2 ,3 ,4 ,5 ,h-c-a-e-r-o-f- -t-s-o-o-b- 21
BOOST_FOREACH虽然是个宏,但内部不使用动态内存分配、虚拟函数、函数指针调用等降低效率收发,其循环执行效率几乎和手写循环同样高效
BOOST_FOREACH不能改变序列的长度,也不能增减序列的元素,否则会导致遍历使用的迭代器失效,发生未定义错误
循环体也可以用break
,continue
,return
函数调用,也可以再嵌入一个BOOST_FOREACH
当然BOOST_FOREACH可以用小写foreach
,它只是个宏定义
支持的序列类型 BOOST_FOREACH是建立在boost.range的概念上,任何符合range概念的容器序列就会自动被支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <boost/array.hpp> #include <boost/circular_buffer.hpp> #include <boost/unordered_set.hpp> void case3 () { using namespace boost::assign; boost::array <int , 5> ar = (list_of(1 ), 2 , 3 , 4 , 5 ); foreach(auto x, ar) cout << x << " " ; cout << endl ; pair<decltype (ar.begin()), decltype (ar.end())> rng(ar.begin(), ar.end() -2 ); foreach(auto x, rng) cout << x << " " ; cout << endl ; boost::circular_buffer<int > cb = list_of(1 )(2 )(3 ); foreach(auto x, cb) cout << x << " " ; cout << endl ; boost::unordered_set <double > us = list_of(3.14 )(2.717 )(0.618 ); foreach(auto x, us) cout << x << " " ; cout << endl ; }
存在的问题 他是宏定义所以一旦有”含逗号的模板”就会失效
1 BOOST_FOREACH(pair<int , string > x, m);
可使用下面解决方案
1 2 3 BOOST_FOREACH(BOOST_INDENTITY_TYPE((pair<int , string >)) x, map ){ ... }
minmax minmax是对c++98标准中的算法std::min/max和std::min_element/max_element的增强
minmax()函数改进了std::min/max函数,可同时返回两个参数的最大最小值,位于名字空间boost,为了使用minmax组件需要包含头<boost/algorithm/minmax.hpp>
1 2 #include <boolst/algorithm/minmax.hpp> usering namespace boost;
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> using namespace std ;#include <boost/algorithm/minmax.hpp> void case1 () { std ::cout << std ::min(200 , 12 ) << std ::endl ; std ::cout << std ::max(200 , 12 ) << std ::endl ; auto x = boost::minmax(1 , 2 ); std ::cout << boost::get<0 >(x) << " " << boost::get<1 >(x); std ::cout << std ::endl ; } 12 200 1 2
存在的问题 minmax()在内部使用make_tuple和cref来生成比较结果的tuple,但并没有使用名字空间的boost限定,这导致在与C++11标准库混用时可能产生问题
1 2 3 4 5 6 7 8 9 10 11 12 void case2 () { std::string s1("5000"), s2("123"); auto x = std ::minmax(s1, s2) ; cout << get<0 >(x) << " " << get<1 >(x) << endl ; auto y = std ::minmax({3 ,4 ,8 ,1 }) ; cout << get<0 >(y) << " " << get<1 >(y); }
所以推荐使用std::minmax
minmax_element minmax_element并不是一个算法,而是一个算法族,包括first_min_element()、last_min_element()、first_min_first_max_element()、first_min_last_max_element()等一系列算法
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> using namespace std ;#include <boost/algorithm/minmax_element.hpp> void case1 () { vector <int > v = {633 , 90 , 67 ,83 , 2 , 100 }; auto x = boost::minmax_element(v.begin(), v.end()); cout << "min : " << *x.first << endl ; cout << "max : " << *x.second <<endl ; } min : 2 max : 633 void case2 () { vector <int > v = {3 ,5 ,2 ,2 ,10 ,9 ,10 ,8 }; decltype (v.begin()) pos; pos = boost::first_min_element(v.begin(),v.end()); assert(pos - v.begin() == 2 ); pos = boost::last_min_element(v.begin(),v.end()); assert(pos - v.begin() == 3 ); auto x = boost::first_min_last_max_element(v.begin(),v.end()); assert(x.first - v.begin() == 2 && x.second - v.begin() == 6 ); }
algorithm algorithm库是一个算法的集合,包含了C++11/14算法的实现和很多有用的小算法,如all_of,none_of,equal,KMP,Boyer-Moore等等
clamp clamp算法位于名字空间boost::algorithm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <iostream> using namespace std ;#include <boost/algorithm/clamp.hpp> using namespace boost::algorithm;void case1 () { assert(clamp(5 , 1 , 10 ) == 5 ); assert(clamp(5 , 5 , 10 ) == 5 ); assert(clamp(5 , 1 , 5 ) == 5 ); assert(clamp(5 , 10 , 15 ) == 10 ); assert(clamp(5 , 0 , 4 ) == 4 ); }
clamp_range 可以操作迭代器或容器的版本clamp_range,它可以对一组元素执行clamp算法,然后把结果写入到一个输出迭代器
1 2 3 4 5 6 7 8 void case2 () { vector <int > v = {2 ,4 ,6 ,8 ,10 }; clamp_range(v, ostream_iterator<int >(cout , "," ), 3 , 9 ); }
hex和unhex hex算法用来执行十六进制编码和解码,包含两个互逆操作:hex和unhex
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <iostream> using namespace std ;#include <boost/algorithm/hex.hpp> using namespace boost::algorithm;void case1 () { string s; hex("123" , ostream_iterator<char >(cout )); cout << endl ; hex("ABC" , std ::back_inserter(s)); cout << s << endl ; unhex(s, ostream_iterator<char >(cout )); cout << endl ; hex("+-*/" , ostream_iterator<char >(cout )); cout << endl ; hex_lower("+-*/" , ostream_iterator<char >(cout )); cout << endl ; } 313233 414243 ABC 2B 2D2A2F2b 2d2a2f
容器和数据结构 array 包装C++语言内建数组,为期提供标准的容器接口
操作 array重载了[]操作符,可以像普通数组和std::vector一样用下标访问内部元素
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> #include <boost/array.hpp> using namespace boost;void case1 () { array <int , 10> ar; ar[0 ] = 1 ; ar.back() = 10 ; assert(ar[ar.max_size() - 1 ] == 10 ); ar.assign(777 ); for (auto x : ar) { std ::cout << x << "," ; } int *p = ar.c_array(); *(p + 5 ) = 253 ; std ::cout << ar[5 ] << std ::endl ; ar.at(8 ) = 666 ; std ::sort(ar.begin(), ar.end()); }
缺陷
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <boost/assign.hpp> void case2 () { array <std ::string , 3> ar = {"alice" ,"bob" , "carl" }; int a[10 ] = {0 }; array <int , 10> ar1 = {0 }; assert(std ::equal(ar1.begin(), ar1.end(), a)); array <std ::string , 3> ar2 = {"racer" }; assert(ar2.at(1 ).empty()); using namespace boost::assign; array <int , 3> arr(list_of(2 )(4 )(6 )) ; for (auto i = 0u ;i< arr.size() ;++i) { std ::cout << arr[i] << "," ;} }
对比C++11标准
dynamic_bitset vector 对元素类型bool的vector特化,内部并不真正存储bool,而是以bit来压缩保存,使用代理技术操作bit,造成的结果是很像容器
dynamic_bitset几乎和std::bitset相同,包括接口和行为,唯一区别是dynamic_bitset的大小在构造函数中由参数指定的,而且运行时是可变的
创建与赋值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <boost/core/lightweight_test.hpp> #include <boost/utility.hpp> #include <boost/dynamic_bitset.hpp> using namespace boost;void case1 () { dynamic_bitset<> db1; dynamic_bitset<> db2(10 ); dynamic_bitset<> db3(0x16 , BOOST_BINARY(10101 )); dynamic_bitset<> db4(string ("0100" )); dynamic_bitset<> db5(db3); dynamic_bitset<> db6; db6 = db4; cout << hex << db5.to_ulong() << endl ; cout << db4[0 ] << db4[1 ] << db4[2 ] << endl ; }
容器操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 void case2 () { dynamic_bitset<> db; db.resize(10 , true ); cout << db << endl ; db.resize(5 ); cout << db << endl ; { dynamic_bitset<> db(5 ,BOOST_BINARY(01110 )); cout << db << endl ; assert(db.size() == 5 ); db.clear(); assert(db.empty()&& db.size()==0 ); } assert(dynamic_bitset<>(64 ).num_blocks()==1 ); assert(dynamic_bitset<>(65 ).num_blocks()==2 ); { dynamic_bitset<> db(5 ,BOOST_BINARY(01001 )); db.push_back(true ); assert(db.to_ulong() == BOOST_BINARY_UL(101001 )); } { dynamic_bitset<> db(5 ,BOOST_BINARY(01001 )); db.append(BOOST_BINARY(101 )); assert(db.size() == sizeof (unsigned long )*8 + 5 ); cout << db << endl ; } }
位运算与比较运算 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void case3 () { dynamic_bitset<> db1(4 , BOOST_BINARY(1010 )); db1[0 ] &= 1 ; db1[1 ] ^= 1 ; cout << db1 << endl ; dynamic_bitset<> db2(4 , BOOST_BINARY(0101 )); assert(db1 > db2); cout << (db1 ^ db2) << endl ; cout << (db1 | db2) << endl ; }
位运算 除了使用operator[]直接访问容器内元素外,dynamic_bitset还有数个成员函数用于测试或者翻转二进制位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 void case4 () { dynamic_bitset<> db(4 , BOOST_BINARY(0101 )); assert(db.test(0 ) && !db.test(1 )); assert(db.any() && !db.none()); assert(db.count() == 2 ); { dynamic_bitset<> db(4 , BOOST_BINARY(0101 )); db.flip(); assert(db.to_ulong() == BOOST_BINARY(1010 )); db.set (); assert(!db.none()); db.reset(); assert(!db.any() ); db.set (1 , 1 ); assert(db.count() == 1 ); } { dynamic_bitset<> db(5 , BOOST_BINARY(00101 )); auto pos = db.find_first(); assert(pos == 0 ); pos = db.find_next(pos); assert(pos == 2 ); } }
unordered 散列容器,hash容器,非常重要的容器类型,比二叉树的存储方式提高更高的访问效率
c++98并未规定散列容器,等c++标准委员会整理的时候,hash_xxx名字都被占领了,于是就用了unordered_xxx的名字…..
boost.unordered库完全符合c++11标准的散列容器实现
只需要头部
1 2 3 #include <boost/unordered_set.hpp> #include <boost/unordered_map.hpp> using namespace boost;
散列集合简介(unordered_set) unordered库提供了两个散列集合类unordered_set和unordered_multiset
散列集合的用法 unordered_set具有与std::set相同的功能,可以用size()获得容器的大小,用empty()判空,支持比较操作符,可以用count(),find(),大多数应用std::Set的场景都能用unordered_set替换
由于hash容器是无序,所以不能使用binary_search、lower_bound和upper_bound用于已序区间的算法
基本用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <iostream> using namespace std ;#include <boost/assign.hpp> #include <boost/unordered_set.hpp> #include <boost/unordered_map.hpp> using namespace boost;void case1 () { unordered_set <int > s = {1 ,2 ,3 ,4 ,5 }; for (auto x : s) { cout << x << " " ; } cout << endl ; cout << s.size() << endl ; s.clear(); cout << s.empty() << endl ; s.insert(8 ); s.insert(45 ); cout << s.size() << endl ; cout << *s.find(8 ) << endl ; s.erase(45 ); using namespace boost::assign; unordered_set <int > us1 = list_of(1 )(2 )(3 ); unordered_set <int > us2 = list_of(3 )(2 )(1 ); assert(us1 == us2 ); }
对比C++11标准 unordered_set与c++11标准定义的hash散列容器完全兼容
支持转移(move)语义,和新的emplace方法
emplace两中形式:
emplace_hint():使用若干参数创建对象插入到容器中,避免拷贝对象的代价
emplace(直接放入)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void case2 () { typedef complex <double > complex_t ; unordered_set <complex_t > s; s.emplace(1.0 , 2.0 ); s.emplace(3.0 , 4.0 ); for (auto & x : s) { cout << x << "," ;} cout << endl ; s.emplace_hint(s.begin(), 5.0 , 6.0 ); for (auto & x : s) { cout << x << "," ;} }
散列映射(unordered_map) unordered库提供两个散列映射unordered_map和unordered_multimap,他们的接口和用法与c++11标准关联容器map/multimap相同
只是内部使用了散列表代替了二叉树实现,比较谓语用equal_to<>
散列映射基本用法 因为unorder_multomap有重复的key-value映射,因此不提供operator[]操作符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void case3 () { using namespace boost::assign; unordered_map <int , string > um = map_list_of(1 ,"one" )(2 , "two" )(3 , "three" ); um.insert(make_pair(10 ,"ten" )); cout << um[10 ] << endl ; um[11 ] = "eleven" ; um[15 ] = "fifteen" ; auto p = um.begin(); for (; p != um.end(); ++p) { cout << p->first << "-" << p->second << "," ; } cout << endl ; um.erase(11 ); cout << um.size() << endl ; unordered_map <int , string > um1 = map_list_of(1 ,"11" )(2 ,"22" ); unordered_map <int , string > um2 = map_list_of(1 ,"one" )(2 ,"two" ); assert(um1 != um2); }
对比c++11标准 unordered_map同样支持emplace方法,但因为它持有的是pair,所以用法不同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void case4 () { typedef complex <double > complex_t ; typedef unordered_map <int ,complex_t > um_t ; um_t s; s.emplace(boost::unordered::piecewise_construct, make_tuple(1 ),make_tuple(1.0 , 2.0 )); s.emplace(boost::unordered::piecewise_construct, make_tuple(3 ),make_tuple(3.0 , 4.0 )); for (auto & x: s) { cout << x.first << "<->" << x.second << "," ; } cout << endl ; s.emplace_hint(s.begin(), boost::unordered::piecewise_construct, make_tuple(5 ),make_tuple(5.0 , 6.0 )); for (auto & x: s) { cout << x.first << "<->" << x.second << "," ; } }
bimap c++标准提供了映射型容器map和multi_map,它们就像关联数组,一个元素的key映射到另一个元素的value, 但是这种映射是单向的,只能key到value,而不能反过来
boost.bimap扩展了标准库的映射型容器,提供双向映射的能力,功能强大
类摘要
基本用法 bimap容纳两个类型的元素,这个关系有左视图和右视图两个视图,分别用left和right访问,相当于两个不同方向的std::map,其用法也同std::map一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <boost/bimap.hpp> using namespace boost;void case1 () { bimap<int , string > bm; bm.left.insert(make_pair(1 , "111" )); bm.left.insert(make_pair(2 , "222" )); bm.right.insert(make_pair("string" , 10 )); bm.right.insert(make_pair("bimap" , 20 )); bm.right.insert(make_pair("bimap" , 2 )); for (auto pos = bm.left.begin(); pos != bm.left.end();++pos) { cout << "left[" << pos->first << "]=" << pos->second << endl ; } { bimap<int , string > bm; typedef decltype(bm)::value_type vt; bm.insert(vt(3 , "333" )); } }
集合类型值 bimap以自然的方式扩展了映射的语义,将std::map的key/value值的映射关系分为左右两个集合映射关系
bimap定义的集合类型有如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 using namespace boost::bimaps;bimap<int , unordered_set_of< string > > bm1; bimap< multiset_of<int >, multiset_of< string > > bm2; bimap< unordered_set_of<int >, list_of< string > > bm3; bimap< vector_of<int >, unconstrained_set_of< string > > bm4; template <typename T>void print_map (T &m) { for (auto & x : m) { cout << x.first << "<-->" << x.second << endl ; } } void case2 () { bimap<unordered_multiset_of<int >, unordered_multiset_of< string > > bm; bm.left.insert(make_pair(1 , "111" )); bm.left.insert(make_pair(2 , "222" )); bm.left.insert(make_pair(2 , "555" )); bm.right.insert(make_pair("string" , 10 )); bm.right.insert(make_pair("bimap" , 20 )); bm.right.insert(make_pair("bimap" , 2 )); print_map(bm.left); { bimap<set_of<int >, vector_of<string > > bm; bm.left.insert(make_pair(1 , "111" )); bm.left[2 ] = "222" ; bm.left[300 ] = "bimap" ; print_map(bm.left); } }
使用标签类型 bimap的左视图和右视图分别用bimap.left和bimap.right来访问 但是缺乏具体的含义不能表达左右视图的用途
bimap可以使用bimaps::tagged类给左组和右组数据在语法层面上贴”标签”,从而更清晰说明左右视图的含义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 bimap<tagged<int , struct id >, vector_of <string> > bm11 ; bimap<multiset_of<tagged<int , struct id > >, unordered_set_of <tagged< string, struct name> > > bm12 ; void case3 () { bimap<tagged<int , struct id >, tagged < string, struct name> > bm ; bm.by<id>().insert(make_pair(1 , "samus" )); bm.by<id>().insert(make_pair(2 , "adam" )); bm.by<name>().insert(make_pair("link" , 10 )); bm.by<name>().insert(make_pair("zelda" , 11 )); print_map(bm.by<name>()); }
使用assign库 用以下语法赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <boost/assign.hpp> void case4 () { using namespace boost::bimaps; typedef bimap<multiset_of<int >, vector_of<string > > bm_t ; bm_t bm = assign::list_of<bm_t ::relation>(1 , "111" )(2 , "222" ); assign::insert(bm.left)(3 , "333" )(4 , "444" ); assign::push_back(bm.right)("555" , 5 )("666" , 6 ); auto left_pos = bm.left.find(3 ); auto right_pos = bm.project_right(left_pos); cout << "right:[" << right_pos->first << "]=" << right_pos->second; }
查找与替换 以键值为索引查找元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <boost/bimap/support/lambda.hpp> void case5 () { using namespace boost::bimaps; typedef bimap<int , string > bm_t ; using namespace boost::assign; bm_t bm = assign::list_of<bm_t ::relation> (1 , "mario" )(2 , "peach" ); auto pos = bm.left.find(1 ); cout << "[" << pos->first << "]=" << pos->second << endl ; auto pos2 = bm.right.find("peach" ); cout << "[" << pos2->first << "]=" << pos2->second << endl ; pos = bm.left.find(1 ); bm.left.modify_key(pos,_key = 111 ); bm.left.modify_data(pos,_data = "luigi" ); print_map(bm.left); } [1 ]=mario [peach]=2 2 <-->peach111 <-->luigi
投射 bitmap提供了三个成员函数 project_left,project_right,project_up可以把bimap的迭代器分别投射到左、右、关系视图上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void case6 () { using namespace boost::bimaps; typedef bimap<set_of<tagged<int ,struct id > >, multiset_of < tagged<string,struct name> > > bm_t ; using namespace boost::assign; bm_t bm = assign::list_of<bm_t ::relation>(1 , "mario" )(2 , "peach" ); insert(bm.by<id>())(3 , "wario" )(4 , "luigi" ); insert(bm.by<name>())("yoshi" , 5 )("olima" , 6 ); auto right_pos = bm.by<name>().find("yoshi" ); auto left_pos = bm.project<id>(right_pos); ++left_pos; cout << "left:[" << left_pos->get<id>() << "]=" << left_pos->get<name>(); } left:[6 ]=olima
circular_buffer circular_buffer实现了循环缓冲区的数据结构,支持标准的容器操作(如push_back)但大小是固定的,当到达容器末尾时将自动循环利用容器另一端空间
类摘要
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <boost/assign.hpp> #include <boost/circular_buffer.hpp> using namespace boost;void case1 () { circular_buffer<int > cb(5 ); assert(cb.empty()); cb.push_back(1 ); cb.push_front(2 ); assert(cb.front() == 2 ); cb.insert(cb.begin(), 3 ); for (auto pos = cb.begin(); pos != cb.end();++pos) { cout << *pos << "," ; } cout << endl ; cb.pop_front(); assert(cb.size() == 2 ); cb.push_back(); assert(cb[0 ] = 2 ); circular_buffer<int > cb1 = (assign::list_of(1 ),2 ,3 ); circular_buffer<int > cb2 = (assign::list_of(3 ),4 ,5 ); circular_buffer<int > cb3 = cb1; assert(cb1 < cb2); assert(cb1 == cb3); }
环形缓冲区 circular_uffer
特殊之处在于它内部存储数据的方式,内部空间不是动态增长的,而是循环使用的
可以把circular_buffer内部想象成一个首尾相连的环,当元素数量达到容器的容量上限时将自动重用最初的空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 template <typename T>void print (T& cb) { for (auto & x: cb) { cout << x << "," ; } cout << endl ; } void case2 () { circular_buffer<int > cb = (assign::list_of(1 ),2 ,3 ); print(cb); cb.push_back(4 ); print(cb); cb.push_back(5 ); print(cb); cb.pop_front(); print(cb); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void case3 () { circular_buffer<int > cb =(assign::list_of(1 ),2 ,3 ,4 ,5 ); assert(cb.full()); print(cb); int *p = cb.linearize(); assert(p[0 ]== 1 && p[3 ] == 4 ); assert(cb.is_linearized()); cb.rotate(cb.begin()+ 2 ); print(cb); }
空间优化型缓冲区 circular_buffer在创建时一次性分配所需内存,这是标准容器的通常做法,但对于循环缓冲区数据结构不适合,因此circular_buffer提供circular_buffer_space_optimized类,是circular_buffer适配器,只有在确实需要时才分配内存空间,而且当容器内元素减少时自动释放内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void case4 () { using namespace boost::assign; circular_buffer_space_optimized<int > cb( 10 ); push_back(cb)(1 ),2 ,3 ,4 ; assert(cb.size() == 4 ); assert(cb.capacity() == 10 ); cb.resize(100 , 10 ); assert(cb.size() == cb.capacity()); }
tuple tuple元组,定义了一个有序有固定数目的元素容器,每个元素的类型都可以不相同,这与其他容器有区别
已被收入c++11标准,tuple位于boost::tuples名字空间,大部分功能被using语句引入名字空间boost
1 2 #include <boost/tuple/tuple.hpp> using namespace boost;
最简单的tuple:pair 1 2 3 4 5 6 7 template <typename T, typename U>struct pair { T first; U second; } pair<int , string > a_pair;
类摘要
创建与赋值 tuple最多支持10个模板类型参数,也就是它最多容纳10个不同类型的元素
tuple对元素的类型没有特殊的要求,但如果类型不支持缺省构造或者赋值操作,那么tuple也会相应地缺失功能
make_tuple
为了方便创建tuple对象,tuple库提供了与make_park类似的make_tuple()函数
它同样可以根据参数类型推到出要创建的tuple类型,默认的类型是非引用类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <boost/core/ignore_unused.hpp> #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_comparison.hpp> #include <boost/tuple/tuple_io.hpp> using namespace boost;typedef tuple<int , std ::string > my_tuple1;typedef tuple<int , my_tuple1> my_tuple2;typedef tuple<void > no_instance_t1;typedef tuple<double (int )> no_instance_t2;void case1 () { typedef tuple<int , std ::string , double > my_tuple; my_tuple t1; my_tuple t2 (1 , "123" ) ; my_tuple t3 (t1) ; t2 = t3; int x = 10 ; tuple<int &> t4(x); { tuple<void *> t1; tuple<double (*)(int )> t2; } { boost::make_tuple(2 , 3.0 ); boost::make_tuple(std ::string (), std ::vector <int >()); int i; std ::string s; tuple<int , std ::string &> t1 = boost::make_tuple(i, boost::ref(s)); tuple<const int &, std ::string &> t2 = boost::make_tuple(boost::cref(i),boost::ref(s)); boost::ignore_unused(t1, t2); } }
访问元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void case2 () { auto t = make_tuple(1 , "char[]" , 100.0 ); assert(t.get<0 >() == 1 ); assert(t.get<2 >() == 100.0 ); std ::cout << t.get<1 >(); std ::cout << ++t.get<0 >(); std ::cout << std ::endl ; get<0 >(t); get<1 >(t); }
比较操作 tuple 全面支持比较操作,包括相等和不等的各种测试,他将比较操作符转发到内部的各个元素进行比较,因此要求tuple元素必须能够执行比较操作,否则会引发编译错误
1 2 3 4 5 6 7 8 9 10 11 void case3 () { typedef boost::tuple<int ,double ,std ::string > my_tuple; my_tuple t1 = boost::make_tuple(1 , 100.0 , std ::string ("abc" )); my_tuple t2 = boost::make_tuple(1 , 200.0 , std ::string ("def" )); assert(t1 < t2); my_tuple t3 (t2) ; assert(t2 == t3); }
输入输出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 void case4 () { typedef boost::tuple<int ,double ,std ::string > my_tuple; my_tuple t1 (1 , 2.0 , "string" ) ; std ::cout << t1 << std ::endl ; std ::cout << tuples::set_open('[' ) << tuples::set_close(']' ); std ::cout << tuples::set_delimiter(',' ); std ::cout << t1 << std ::endl ; } ``` - set_open(char ) : 设置tuple开始时的字符 - set_close(char ) : 设置tuple结束时的字符 - set_delimiter(char ) : 设置元素之间的分隔符 ### 联结变量 类似make_tuple()的函数tie(),可以把变量联结到tuple上,生成一个元素类型全是引用的tuple ```c++ typedef tuple<int ,double ,std ::string > my_tuple;my_tuple func () { return boost::make_tuple(1 , 2.0 , "string" );}void case5 () { int i;double d;std ::string s; tie(i, d, s) = func(); std ::cout << i; tie(tuples::ignore, d, tuples::ignore) = func(); }
应用于assign库 tuple_list_of,初始化元素类型为tuple的容器,用法类似 map_list_of和pari_list_of
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <boost/assign.hpp> void case6 () { typedef boost::tuple<int ,double ,std ::string > my_tuple; using namespace boost::assign; std ::vector <my_tuple> v = tuple_list_of(1 , 1.0 , "123" )(2 , 2.0 , "456" ); assert(v.size() == 2 ); v += boost::make_tuple(3 , 3.0 , "789" ),boost::make_tuple(4 , 4.0 , "abc" ); assert(v.size() == 4 ); }
内部结构
any any一个特殊容器,只能容纳一个元素,但这个元素可以是任意类型的int,double,string,标准容器或者任何的自定义类型
在需要的时候将它取出,这种功能与shared_ptr有些类似,但any是类型安全,已经被加入c++17的标准
访问元素 any类本身不提供任何对内部元素访问的函数,而是提供了一个friend函数any_cast(),模仿了标准库的转型操作符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void case1 () { any a (10 ) ; int n = any_cast<int >(a); assert(n == 10 ); any_cast<int &>(a) = 20 ; assert(any_cast<int >(a) == 20 );; try { any a; any_cast<int >(a); } catch (boost::exception&) { cout << current_exception_diagnostic_information(); } any a1, a2(2.0 ); assert(any_cast<int *>(&a1) == nullptr ); assert(any_cast<string *>(&a2) == nullptr ); }
操作函数 any类很有用,操作函数为any_case(),但自己可以编写一些辅助类和函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 template <typename T>bool can_case (any &a) { return typeid (T) == a.type(); } template <typename T>T& get (any &a) s { BOOST_ASSERT(can_case<T>(a)); return *any_cast<T>(&a); } template <typename T>T* get_pointer (any &a) { BOOST_ASSERT(can_cast<T>(a)); return any_cast<T>(&a); }
保存指针 any可以持有原始指针,但怕不安全,希望智能指针包裹,这样any析构的时候智能指针会自动调用delete,从而安全释放资源
不是所有智能指针都可以作为any寸处对象,scoped_ptr不行,因为它不能被拷贝,不符合any类型要求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 template <typename T>bool can_cast (any &a) { return typeid (T) == a.type();}template <typename T>T& get (any &a) { assert(can_cast<T>(a)); return *any_cast<T>(&a); } template <typename T>T* get_pointer (any &a) { assert(can_cast<T>(a)); return any_cast<T>(&a); } template <typename T>any make_ptr_any (T *p = 0 ) { return any(boost::shared_ptr <T>(p));}template <typename T>boost::shared_ptr <T>& get_shared(any &a) { assert(can_cast<boost::shared_ptr <T> >(a)); return *any_cast<boost::shared_ptr <T> >(&a); } void case3 () { any a; int x = 1 ; a = x; assert(can_cast<int >(a)); get<int >(a) = 10 ; *get_pointer<int >(a) = 20 ; a = make_ptr_any<string >(new string ("long" )); cout << *get_shared<string >(a) << endl ; a = make_ptr_any<vector <int > >(new vector <int >); }
输出 any 不支持内部值流输出或者转为字符串,这是它所缺乏又很常用的功能,特别是在调用的时候
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 template <typename T > struct any_print { void operator () (any &a) try { cout << *any_cast<T>(&a) << endl ; } catch (bad_any_cast &) { cout << "print error" << endl ; } }; template <typename T >struct any_print <boost::shared_ptr<T> >{ void operator () (any &a) try { cout << **any_cast<boost::shared_ptr <T> >(&a) << endl ; } catch (bad_any_cast &) { cout << "print error" << endl ; } }; void case4 () { any a; a = 10 ; any_print<int >()(a); auto ps = boost::make_shared<string >("metroid" ); a = ps; any_print< boost::shared_ptr <string > >()(a); }
应用于容器 any可以容纳一个不同类型的元素,所以当应用于容器可以如下做法
1 2 3 4 5 6 7 8 9 10 11 12 void case5 () { vector <any> v; v.push_back(10 ); v.push_back(1.414 ); v.push_back(boost::shared_ptr <int >(new int (100 ) )); using namespace boost::assign; vector <any> v2 = list_of<any>(10 )(0.618 )(string ("char" )); cout << v2.size(); }
variant variant与any有些类似,是一种可变类型,是对c/C++中union概念的增强和扩展,普通的union只有持有POD(普通数据类型),而不能持有如string、vecotr等复杂类型
1 2 #include <boost/variant.hpp> using namespace boost;
类摘要
访问元素 variant的操作要比any方便,能够直接访问元素的值
1 2 3 4 5 6 7 8 9 10 11 12 #include <boost/assign.hpp> #include <boost/variant.hpp> using namespace boost;void case1 () { variant<int , float , string > v; v = "123" ; cout << v << endl ; }
用法 get操作室必须查询variant当前值的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void case2 () { typedef variant<int , double , string > var_t ; var_t v(1 ); v = 2.13 ; assert(v.type() == typeid (double )); var_t v2("string type" ); cout << get<string >(v2); v2 = v; cout << get<int >(var_t (108 )); }
访问器 在运行时通过type()检测variant类型,再施以必要的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void case3 () { typedef variant<int , double , string > var_t ; var_t v; assert(v.type() == typeid (int )); assert(v.which() == 0 ); v = "variant demo" ; cout << *get<string >(&v) << endl ; try { cout << get<double >(v) << endl ; } catch (bad_get &) { cout << "bad_get" << endl ; } }
variant基于访问者模式提供了模板类static_visitor,解耦了variant的数据存储和访问操作,把访问操作集中在访问器类 易于增加新的访问操作,使两者彼此独立的变化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 struct var_print : public static_visitor<>{ template <typename T> void operator () (T &i) const { i *= 2 ; cout << i << endl ; } void operator () (vector <int > &v) const { v.reserve(v.size()*2 ); copy(v.begin(),v.end(),back_inserter(v)) ; for (auto & x : v) { cout << x << "," ; } cout << endl ; } }; void case4 () { typedef variant<int , double , vector <int > > var_t ; var_t v(1 ); var_print vp; apply_visitor(vp, v); v = 3.414 ; apply_visitor(vp, v); vector <int > tmp = {1 ,2 }; v = tmp; apply_visitor(vp, v); auto vp2 = apply_visitor(vp); vp2(v); }
与any的区别 variant很像any,容纳一个可变类型元素,但variant是有界类型,元素类型范围由用户指定,而any是误解类型,可以容纳任一类型
很多人认为variant没有存在的必要,但是any任意类型自由往往会让程序员犯错,灵活付出的代价就是更多的安全检查代价,所以variant有了出现的必要,在编译器进行类型检查,充分利用c++静态强类型语言的好处
multi_array C++标准库提供了string,array和vector,但是他们都是一维数组, vector<vector>虽然可以用,但是不方便
multi_array可以解决这个问题,多维容器,高效实现了STL风格的多维数组,比原始多维数组或vector of vector更好
类摘要
用法 multi_array的模板参数很像标准容器,除了容纳的元素类型外,多了一个维数的末班参数,用来指定多维数组的维数
1 2 3 multi_array<int , 3 > ma;
extents_gen类和预定义一个实例extents,重载了operator[],用起来就像一个原始多维数组
1 multi_array<int ,3 > ma(extents[2 ][3 ][4 ]);
num_dimensions()获得multi_array总维数,返回值就是模板参数中的NumDims
multi_array重载了operator[]操作符,使用普通数组那样访问内部元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <boost/assign.hpp> #include <boost/multi_array.hpp> using namespace boost;void case1 () { multi_array<int , 3 > ma(extents[2 ][3 ][4 ]); auto shape = ma.shape(); for (size_t i = 0 ; i < ma.num_dimensions(); ++i) { cout << shape[i] << "," ; } cout << endl << ma.num_elements() << endl ; for (int i = 0 , v = 0 ; i < 2 ; ++i) for (int j = 0 ; j < 3 ;++j) for (int k = 0 ;k < 4 ;++k) { ma[i][j][k] = v++; } for (int i = 0 ; i < 2 ; ++i) { for (int j = 0 ; j < 3 ;++j) { for (int k = 0 ;k < 4 ;++k) { cout << ma[i][j][k] << "," ; } cout << endl ; } cout << endl ; } std ::array <size_t , 3> idx = {0 ,1 ,2 }; ma(idx) = 10 ; cout << ma(idx) << endl ; }
多维数组另一种形式提供了位置索引序列,传递给operator()可直接访问元素,某些时候比operator[]效率更高
1 2 3 array <size_t ,3> idx = {0 , 1 , 2 };ma(idx) = 10 ; cout << ma(idx);
改变形状和大小 multi_array可以在运行时使用
成员函数reshape()改变多维数组的形状,即变动各个维度的大小,但总维数和元素数量保持不变 变动前的维度乘积和变动后的维度乘积必须相同
成员函数resize()改变某维度的数组大小,但是维度无法改变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 multi_array<int , 3 > ma(extents[2 ][3 ][4 ]); assert(ma.shape()[0 ] == 2 ); std ::array <int , 3> arr = {4 ,3 ,2 };ma.reshape(arr); assert(ma.shape()[0 ] == 4 ); ma.resize(extents[2 ][9 ][9 ]); assert(ma.num_elements() == 2 *9 *9 ); assert(ma.shape()[1 ] == 9 );
创建子视图 multi_array库允许用户为多维数组创建一个只查看其中一部分数据的子视图,子视图既可以与原数组拥有相同维数,也可以少于原维数,被称为切片(slice)
indices用法类似extents对象,重载了operator[]操作符来限定范围,但参数不是单个整数,而是一个multi_array<T,N>::index_range对象,用来指定多维数组中抽取的维度范围
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void case3 () { typedef multi_array<int , 2 > ma_type; multi_array<int , 2 > ma(extents[3 ][4 ]) ; typedef ma_type::index_range range; auto view = ma[indices[range(0 ,2 )][range(0 ,2 )] ]; cout << view.num_elements() << endl ; for (int i = 0 ; i < 2 ; ++i) { for (int j = 0 ; j < 2 ;++j) { cout << view[i][j] << "," ; } cout << endl ; } cout << *view.shape() << endl ; }
适配普通数组 multi_array是多维数组常用类,自己管理内存,动态增长,但有时候需要将一维数组适配成多维数组处理
multi_array提供了multi_array_ref和const_multi_array_ref来满足这一需求,把一段连续内存(原始数组)适配多维数组,用法类似smart_ptr的scope_array
适配后不能用动态增长外,其他与multi_array完全相同,支持operator[]访问和reshape改变维度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 void case4 () { int arr[12 ]; for (int i = 0 ;i < 12 ;++i) { arr[i] = i; } multi_array_ref<int , 2 > mar(arr, extents[3 ][4 ]); for (size_t i = 0 ; i < 3 ; ++i) { cout << "(" ; for (size_t j = 0 ;j < 4 ;++j) { cout << mar[i][j]++; cout << (j!=3 ?',' :' ' ); } cout << ")" << endl ; } const_multi_array_ref<int , 2 > cmar(arr, extents[2 ][6 ]); for (size_t i = 0 ; i < 2 ; ++i) { cout << "(" ; for (size_t j = 0 ;j < 6 ;++j) { cout << cmar[i][j]; cout << (j!=5 ?',' :' ' ); } cout << ")" << endl ; } } (0 ,1 ,2 ,3 ) (4 ,5 ,6 ,7 ) (8 ,9 ,10 ,11 ) (1 ,2 ,3 ,4 ,5 ,6 ) (7 ,8 ,9 ,10 ,11 ,12 )
property_tree property_tree 是一个保存了许多个属性值的树形数据结构,可以用类似路径的简单方式访问任意节点的树形 而且每个节点类似STL的风格遍历子节点,property_tree特别适合于应用程序的配置数据处理,可以解析xml,ini,json和info四种格式的文本数据,使用它能够减轻自己的开发配置管理工作
类摘要
basic_ptree接口很像标准容器std::list
两个重要的内部类型定义
self_type是自身类型也是子节点类型
value_type是节点的数据结构,一个std::pair,含属性名(first)和节点自身(second)
读取配置信息 1 2 3 4 5 6 7 8 9 10 11 12 <conf > <gui > 1</gui > <theme id ="001" > matrix</theme > <urls > <url > http://www.url1.com</url > <url > http://www.url2.com</url > <url > http://www.url3.com</url > </urls > <clock_style name ="local" > 24</clock_style > </conf >
要解析这个配置文件,得有read_xml函数解析并初始化ptree 他有两个函数定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/xml_parser.hpp> using namespace boost::property_tree;void case1 () { ptree pt; read_xml("conf.xml" , pt); pt.get<string >("conf.theme" ); pt.get<int >("conf.clock_style" ); pt.get("conf.no_prop" , 100 ); cout << pt.get<string >("conf.theme" ) << endl ; cout << pt.get<int >("conf.clock_style" ) << endl ; cout << pt.get<long >("conf.gui" )<< endl ; cout << pt.get("conf.no_prop" , 100 )<< endl ; auto child = pt.get_child("conf.urls" ); for (auto & x : child) { cout << x.second.get_value<string >() << "," ; } cout << endl ; for (auto & x : pt.get_child("conf.urls" )) { cout << x.second.data() << "," ; } cout << endl ; } matrix 24 1 100 urls comment ,http: urls comment ,http:
写入配置信息 property_tree不仅能够读取配置信息,也可以写入配置信息,操作具有对称性
使用模板成员函数put()可以修改属性树的节点值,如果子节点不存在就当新增节点
1 2 3 4 5 6 7 8 9 10 11 12 13 void case2 () { ptree pt; read_xml("conf.xml" , pt); pt.put("conf.theme" , "Matrix Reloaded" ); pt.put("conf.clock_style" , 12 ); pt.put("conf.gui" , 0 ); pt.add("conf.urls.url" , "http://www.url4.org" ); write_xml(cout , pt); }
其他用法
find寻找某节点值
1 2 3 4 5 6 7 8 ptree pt; read_xml("a.xml" , pt); auto pos = pt.find("a" );cout << pos->second.data() << endl ;
这样的缺点就是没有get的深层次去查找,也没有get方便
json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <boost/property_tree/json_parser.hpp> void case4 () { ptree pt; read_json("conf.json" , pt); cout << pt.get<string >("conf.theme" ) << endl ; cout << pt.get<int >("conf.clock_style" ) << endl ; cout << pt.get<long >("conf.gui" )<< endl ; cout << pt.get("conf.no_prop" , 100 )<< endl ; for (auto x : pt.get_child("conf.urls" )) { cout << x.second.data() << "," ; } }
并发编程 atomic atomic封装了C++11标准定义的原子操作库,封装了不同计算机硬件的底层操作原语, 提供了跨平台的原子操作功能
load显式调用load取值
store显式调用store取值
基本用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void case1 () { atomic<int > a(10 ); assert(a == 10 ); atomic<long > l; l = 100L ; cout << l << endl ; atomic<double > d(2.414 ); cout << d << endl ; } void case2 () { atomic<bool > b{false }; assert(!b.load()); b.store(true ); assert(b); atomic<int > n(100 ); assert(n.exchange(200 ) == 100 ); assert(n == 200 ); }
compare_exchange_weak()和chage_exchange_strong()是exchage()的增强版本 也就是常说的CAS操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void case3 () { atomic<long > l(100 ); long v = 100 ; if (l.compare_exchange_weak(v, 313 )) { assert(l == 313 && v == 100 ); } v = 200 ; auto b = l.compare_exchange_strong(v, 99 ); assert(!b && v == 313 ); l.compare_exchange_weak(v, 99 ); assert(l == 99 && v == 313 ); }
atomic的成员函数storage()可以直接获得atomic内部值的引用,能够以任意方式操作数据 但他也因此无法提供原子保证,在并发环境里我们应该尽量不使用它
整数atomic用法 fetch_xx原酸操作,执行对应的数学运算后,返回原值而不是运算后的值
atomic重载了operator++、operator+=等操作符,这些操作符重载函数内部也是调用了fetch__xx函数,但返回运算后的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <boost/utility.hpp> void case4 () { atomic<int > n(100 ); assert(n.fetch_add(10 ) == 100 ); assert(n == 110 ); assert(++n == 111 ); assert(n++ ==111 ); assert(n == 112 ); assert((n -= 10 ) == 102 ); atomic<int > b{BOOST_BINARY(1101 )}; auto x = b.fetch_and(BOOST_BINARY(0110 )); assert(x == BOOST_BINARY(1101 ) && b == BOOST_BINARY(0100 )); assert((b |= BOOST_BINARY(1001 )) == BOOST_BINARY(1101 )); }
并发顺序一致性 现代多CPU核心并发的环境里,编译器和cpu都有可能打乱指令的执行顺序,虽然可能会获得更高的执行效率, 但是也可能产生副作用,导致程序的流程不一定按照代码的顺序执行
atomic的每个成员函数都有一个meeory_order缺省参数
指定了原子操作的内存顺序要求
memory_order_seq_cst 是最严格的顺序一致性约束 不允许编译器或cpu核心为优化而调整代码和指令的执行顺序 保证并发环境任何cpu核心看到的指令顺序是相同的
如果memory_order修改其他枚举值,name多核心并发执行同一段代码会有不同的执行顺序
下面代码是使用atomic结合恰当的内存顺序,实现的一个高效的引用技术适配器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 #include <boost/intrusive_ptr.hpp> template <typename T>class ref_count //泛型的引用计数{ private : typedef boost::atomic<int > atomic_type; mutable atomic_type m_count{0 }; protected : ref_count() {} ~ref_count() {} public : typedef boost::intrusive_ptr<T> counted_ptr; void add_ref () const { m_count.fetch_add(1 , boost::memory_order_relaxed); } void sub_ref () const { if (m_count.fetch_sub(1 , boost::memory_order_release) == 1 ) { boost::atomic_thread_fence(boost::memory_order_acquire); delete static_cast <const T*>(this ); } } decltype (m_count.load()) count() const { return m_count.load(); } public : template <typename ... Args> static counted_ptr make_ptr (Args&& ... args) { return counted_ptr(new T(std ::forward<Args>(args)...)); } private : friend void intrusive_ptr_add_ref (const T* p) { p->add_ref(); } friend void intrusive_ptr_release (const T* p) { p->sub_ref(); } }; class demo : public ref_count<demo> { public : demo() { cout << "demo ctor" << endl ; } ~demo() { cout << "demo dtor" << endl ; } int x; }; void case6 () { auto p = demo::make_ptr(); p->x = 10 ; assert(p->x == 10 ); assert(p->count() == 1 ); }
thread thread库为c++增加了线程处理能力
很容易创建多线程应用程序,thread库也是高度可移植,兼容了c++11/14标准 支持使用最广泛的POSIX和Windows线程,不需要修改代码就可以在UNIX,Windows操作系统上编译运行
mutex 互斥量,线程同步工具,多线程环境防治多个线程同时操作共享资源,一旦一个线程锁住了互斥量 那么其他线程必须等待它解锁互斥量才能在访问共享资源
mutex在创建后表示一个互斥量
lock用于线程阻塞等待直至获得互斥量的所有权即锁定
unlock解除对互斥量的锁定
try_lock 尝试锁定互斥量,如果锁定成功返回true,否则返回false,它是非阻塞
try_lock_for 增加了时间等待功能,相对时间长度
try_lock_until 增加了时间等待功能,绝对时间点
mutex 用法 普通mutex用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void case1 () { mutex mu; try { mu.lock(); cout << "some operations" << endl ; mu.unlock(); } catch (...) { mu.unlock(); } { lock_guard<mutex> g(mu); cout << "some operations" << endl ; } }
timed_mutex用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void case2 () { timed_mutex mu; auto flag = mu.try_lock_for(100 _ms); if (flag) { cout << "lock timed mutex" << endl ; mu.unlock(); } { if (mu.try_lock_for(100 _ms)) { lock_guard<timed_mutex> g(mu, adopt_lock); cout << "lock timed mutex" << endl ; } } }
lock_guard thread库提供了lock_guard辅助锁互斥量
lock_guard的第二种形式的构造器是为了配合timed_mutex使用 允许传入一个标志,类型为adopt_lock_t的常量,让lock_guard认为线程之前已经锁定了mutex,只负责析构的时候解锁
lock_guard 用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void case2 () { timed_mutex mu; auto flag = mu.try_lock_for(100 _ms); if (flag) { cout << "lock timed mutex" << endl ; mu.unlock(); } { if (mu.try_lock_for(100 _ms)) { lock_guard<timed_mutex> g(mu, adopt_lock); cout << "lock timed mutex" << endl ; } } }
unique_lock thread库还有一个用法更复杂的unique_lock
unique_lock的工作机制和lock_guard相同
但它的构造函数还可以接收其他选项,从而有不同的行为
工厂函数
也可以一次性产生多个unique_lock,以std::tuple返回
1 std ::tuple<unique_lock<Lockable> ...> make_unique_locks(Lockable& ...mtx);
unique_lock 使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <boost/thread/lock_factories.hpp> //工厂函数头文件 template <typename Lockable, typename D>unique_lock<Lockable> my_make_lock(Lockable& mtx, D d) { return unique_lock<Lockable> (mtx, d); } void case3 () { mutex mu; { auto g = make_unique_lock(mu); assert(g.owns_lock()); cout << "some operations" << endl ; } { auto g = make_unique_lock(mu, defer_lock); assert(!g); assert(g.try_lock()); assert(g); cout << "some operations" << endl ; } timed_mutex tm; { auto g = my_make_lock(tm, 100 _ms); if (g) { cout << "lock timed mutex" << endl ; } } auto g = make_unique_locks(mu, tm); assert(std ::tuple_size<decltype (g)>::value == 2 ); }
lock适配器 lock_guard和unique_lock大多数情况搭配mutex使用,用于锁定互斥量 但因为他们是模板类,所以只要符合Lockable概念,也就是有“lock/unlock/try_lock”接口的类 都可以用于lock_guard和unique_lock,这样能够很容易锁定整个对象,实现原子性的事务操作
适配器类需要模板参数指定适配的mutex类型,以继承方式使用 子类自动获取他们的lock接口,然后就可以应用于lock_guard和unique_lock
以下是一个储蓄账户类,记录金额的案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <boost/atomic.hpp> #include <boost/thread/lockable_adapter.hpp> class account final : public lockable_adapter<mutex> { private : boost::atomic<int > m_money{0 }; public : account() {} ~account() {} public : int sum () const { return m_money; } void withdraw (int x) { m_money -= x; } void deposit (int x) { m_money += x; } }; void case4 () { account a; { auto g = make_unique_lock(a); a.deposit(100 ); a.withdraw(20 ); assert(a.sum() == 80 ); } { auto b = make_unique_lock(a, try_to_lock); if (b) { a.withdraw(a.sum()); assert(a.sum() == 0 ); } } }
lockable概念检查类 检查类型是否可锁定
lock函数 除了使用mutex的成员函数或者lock_guard/unique_lock,我们还可以使用两个自由函数
以上两个操作mutex,类似make_unique_locks,可以一次性锁定多个互斥量,而且保证不会出现死锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void case5 () { mutex m1, m2; { auto g1 = make_unique_lock(m1, adopt_lock); auto g2 = make_unique_lock(m2, adopt_lock); lock(m1, m2); } { auto g1 = make_unique_lock(m1, defer_lock); auto g2 = make_unique_lock(m2, defer_lock); try_lock(g1, g2); } }
使用线程 thread实现了操作系统的线程表示,负责启动和管理线程对象与posix线程很相似
thread对象不可拷贝不可比较,但支持c++11标准的转移move语义 有转移构造函数和转移赋值函数,允许从工厂函数产生
某种程度来讲,线程就是在进程的另一个空间里运行的一个函数 因此线程的创建需要传递给thread对象一个无参的可调函数、函数对象、或者lambda/bind表达式, 他必须具有operator()以供线程执行
如果函数不是无参,thread也可以传递
std::terminate结束线程的执行,并不关心线程是否执行完毕,所以如果要保证函数正确运行必须调用join等待线程执行结束,或者调用detach()分离线程体
启动线程 thread不提供start,begin那样的方法,当城管创建一个thread对象就立刻开始执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <boost/bind.hpp> void dummy (int n) { for (int i = 0 ;i < n; ++i); cout << n << endl ; } void case2 () { thread t1 (dummy, 100 ) ; thread t2 (dummy, 500 ) ; this_thread::sleep_for(200 _ms); }
join线程 thread的成员函数joinable可以判断thread对象是否标识了一个可执行的线程体
如果joinable返回true,我们就可以调用成员函数join或者try_join_for/try_join_util()来阻塞等待线程执行结束
两者区别是
join()一直阻塞等待,直到线程技术
try_join_for/try_join_util阻塞等待一定的时间段,然后不管现场是否结束都返回
1 2 3 4 5 thread t1(bind(dummy, 100)); //使用bind表达式启动线程 thread t2 ([]{dummy(500 ) ;}); t1.try_join_for(100 _ms); t2.join();
join可以这样操作thread对象
detach分离线程 detach将thread与线程执行体手动分离,此后thread对象不代表任何线程体 joinable() == false,从而失去了对线程体的控制
1 2 3 4 thread t1 (dummy, 100 ) ; t1.detach(); assert(!t1.joinable());
分离线程将不受影响继续执行,直到函数结束,或者随主进程一起结束
因此当不再需要操作线程体时,可以使用临时对象来启动线程,随机调用它的detach
thread_guard thread库在c++标准之外提供了类似lock_guard的thread_guard辅助类,可以让我们方便地控制thread对象析构时的行为
1 2 3 4 5 6 7 8 9 10 #include <boost/thread/thread_guard.hpp> void case4 () { thread t1 (dummy, 200 ) ; thread t2 (dummy, 300 ) ; thread_guard<detach> g1(t1); thread_guard<> g2(t2); }
scoped_thread thread库还有一个与thread接口功能一致的线程类 scoped_thread,它可以使用模板参数定值析构的动作
1 2 3 4 5 6 7 8 9 10 #include <boost/thread/scoped_thread.hpp> void case4 () { { scoped_thread<detach> t1(dummy, 10 ); scoped_thread<> t2(dummy, 20 ); } this_thread::sleep_for(100 _ms); }
中断线程 boost::thread库提供了非c++11标准的成员函数interrupt()和interruption_requested()
被中断的线程会抛出一个thread_interrupted异常 它是一个空类,不是std::exception或者boost::exception的子类 thread_interrupted异常应该在线程执行函数里捕获并处理 如果线程不处理这个异常,默认动作是中止线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void to_interrupt (int x) try { for (int i = 0 ;i < x; ++i) { cout << i << endl ; this_thread::interruption_point(); } } catch (const thread_interrupted& ){ cout << "thread_interrupted" << endl ; } void case5 () { thread t (to_interrupt,10 ) ; t.interrupt(); assert(t.interruption_requested()); t.join(); }
中断点 线程不是在任意时刻都可以被中断
启用/禁用线程中断 默认情况下线程是允许中断,但是thread库允许进一步控制线程的中断行为
interruption_enable()函数检测当前线程是否允许中断
interruption_requested()函数检测当前线程是否被要求中断
类disable_interruption,在构造时关闭线程中断,析构时回复线程的中断状态
restore_interruption只能在disable_interruption的作用域内使用,它是在构造是临时打开线程的中断状态,在析构时关闭中断状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 void to_interrupt2 (int x) try { using namespace this_thread; assert(interruption_enabled()); for (int i = 0 ;i < x; ++i) { disable_interruption di; assert(!interruption_enabled()); cout << i << endl ; cout << this_thread::interruption_requested() << endl ; this_thread::interruption_point(); restore_interruption ri (di) ; assert(interruption_enabled()); cout << "can interrupted" << endl ; cout << this_thread::interruption_requested() << endl ; this_thread::interruption_point(); } assert(interruption_enabled()); } catch (const thread_interrupted& ){ cout << "[thread_interrupted]" << endl ; } void case6 () { thread t (to_interrupt2,10 ) ; t.interrupt(); assert(t.interruption_requested()); t.join(); }
thread_group thread库提供了thread_group用于管理一组现场,就像一个线程池
内部是靠一个std::list来容纳创建的thread对象
create_thread是一个工厂函数,可以创建thread对象并允许线程,同时加入内部的list
但是它不支持thread构造函数的传参,这里要用lambda或者bind包装执行函数
成员函数is_this_thread_in和is_thread_in可以判断当前线程或者某个线程是否属于本线程组
join_all和interrupt_all来操作list李所有线程对象等待或中断这些线程
1 2 3 4 5 6 7 void case7 () { thread_group tg; tg.create_thread(bind(dummy, 100 )); tg.create_thread(bind(dummy, 200 )); tg.join_all(); }
call_once 多线程环境中初始化函数正确调用一次,所以thread库提供了仅调用一次机制call_once
这个机制首先要使用一个once_flag对象,它将作为初始化标志,然后在使用call_once来调用函数,完成仅一次的初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int g_count;void init_count (int x) { cout << "should call once." << endl ; g_count = x; } void call_func () { static once_flag once; call_once(once, init_count, 10 ); } void case8 () { (scoped_thread<>(call_func)); (scoped_thread<>(call_func)); }
条件变量 条件变量是一种等待同步机制,可以实现线程间通信,他必须与互斥量配合使用,等待另一个线程中某个事件的发送
thread库提供了两种条件变量对象
condition_variable
condition_variable_any
使用方法,拥有条件变量的线程先锁定互斥量,然后循环检查某个条件,如果条件不满足就wait等待直至条件满足
其他线程处理变量要求条件,当条件满足时调用它的成员函数 notify_one或notify_all
以通知一个或所有正在等待条件变量的线程停止等待继续执行
wait(lock, predicate);
它比普通形式多接受一个谓语函数,当喂鱼predicate不满足时持续等待,也就是当谓语predicate返回true时退出等待
案例
描述生产者->消费者模式,后进先出型缓冲区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 class buffer { private : mutex mu; condition_variable_any cond_put; condition_variable_any cond_get; stack <int > stk; int un_read,capacity; bool is_full () { return un_read == capacity; } bool is_empty () { return un_read == 0 ; } public : buffer(size_t n):un_read(0 ),capacity(n){} void put (int x) { { auto lock = make_unique_lock(mu); cond_put.wait(lock, [this ]{return un_read < capacity;}); stk.push(x); ++un_read; } cond_get.notify_one(); } void get (int *x) { { auto lock = make_unique_lock(mu); cond_get.wait(lock, [this ]{return un_read > 0 ;}); --un_read; *x = stk.top(); stk.pop(); } cond_put.notify_one(); } }; buffer buf (5 ) ;void producer (int n) { for (int i = 0 ;i < n; ++i) { cout << "put " << i << endl ; buf.put(i); } } void consumer ( int n) { int x; for (int i = 0 ;i < n; ++i) { buf.get(&x); cout << "get " << x << endl ; } } void case1 () { thread_group tg; tg.create_thread(bind(producer, 20 )); tg.create_thread(bind(consumer, 10 )); tg.create_thread(bind(consumer, 10 )); tg.join_all(); }
shared_mutex shared_mutex具有mutex全部功能,可以像mutext一样使用lock和unlock 不同于mutext的和recursive_mutex(递归锁),它允许线程获取多个共享所有权和一个专享所有权 实现了读写锁,即多个读线程一个写线程
如果要获得共享所有权需要使用lock_shared(),try_lock_shared(),同时unlock_shared()释放共享所有权 如果要获得专享所有权需要使用lock和unlock
当然 lock_guard和unique_lock也适用于shared_mutex,但只能获得写锁定
读锁定需要使用shared_lock_guard或shared_lock,但很可惜thread库没有make_shared_lock
同样的 适配器 shared_lockable_adapter 可以把一个类适配为符合shared_lockable概念
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <boost/thread/shared_lock_guard.hpp> class rw_data { private : int m_x; shared_mutex rw_mu; public : rw_data():m_x(0 ){} void write () { unique_lock<shared_mutex> g(rw_mu); ++m_x; } void read (int *x) { shared_lock<shared_mutex> g(rw_mu); *x = m_x; } }; void writer (rw_data &d) { for (int i = 0 ;i < 20 ; ++i) { this_thread::sleep_for(3 _ms); d.write(); } } void reader (rw_data &d) { int x; for (int i = 0 ;i < 10 ; ++i) { this_thread::sleep_for(5 _ms); d.read(&x); cout << "reader:" << x << endl ; } } void case7 () { rw_data d; thread_group pool; pool.create_thread(bind(writer,boost::ref(d))); pool.create_thread(bind(writer,boost::ref(d))); pool.create_thread(bind(reader,boost::ref(d))); pool.create_thread(bind(reader,boost::ref(d))); pool.create_thread(bind(reader,boost::ref(d))); pool.create_thread(bind(reader,boost::ref(d))); pool.join_all(); }
future 线程不仅仅要执行一些任务,可能要返回一些计算结果,thread使用future范式提供了异步操作线程返回值的方法
因为这个返回值在线程开始执行时还是不可用的,是一个未来的期待值
future的wait()类似thread.join(),可以阻塞等待线程的执行,直至获取future值
wait_for/wait_until增加了超时的功能,返回futures_status枚举值表示是否可以执行成功
get()用来获取future值,默认调用wait等待线程计算完成,
get只能被调用一次,多次调用会抛出异常future_error
valid(),is_ready(),has_value()和has_exception()分别用来测试future是否可用,是否有值,是否发生了异常
async函数 async用于产生future对象,异步地启动一个线程运行函数,返回future对象,这样可以利用future获取计算结果
为了支持多个future使用,future还提供了c++11标准之外的两个自由函数 wait_for_any和wait_for_all,他们可以阻塞等待多个future对象,知道一个或者所有future对象都可用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 int fab (int n) { if (n == 0 || n == 1 ) { return 1 ; } return fab(n-1 ) + fab(n-2 ); } void case3 () { auto f5 = async(fab, 5 ); auto f7 = async(launch::async, fab, 7 ); cout << f5.get() + f7.get() << endl ; assert(!f5.valid() && !f7.valid()); auto f10 = async(fab, 10 ); auto s = f10.wait_for(100 _ms); if (f10.valid()) { assert(s == future_status::ready); cout << f10.get() << endl ; } vector <boost::future<int >> vec; for (int i = 0 ;i < 5 ; ++i) { vec.push_back(async(fab, i + 10 )); } wait_for_any(vec[3 ], vec[4 ], vec[2 ]); for (auto & x : vec) { if (x.valid()) { cout << x.get() << endl ; } } cout << endl ; }
shared_future future在thread早期版本名字unique_future,因为get只能获取一次,不能被多个线程访问,不够方便
shared_future是future增强版本,类似future,但是可以线程安全地多次调用get获取计算结果
async()函数可以返回shared_future对象,但需要明确声明类型,例如
1 shared_future<int > f5 = async(fab, 5 );
使用future的shared函数可以产生shared_future对象,或者明确声明类型也可行
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void case4 () { auto f5 = async(fab, 5 ).share(); auto func = [](decltype (f5) f){ cout << "[" << f.get() << "]" ; }; async(func, f5); async(func, f5); this_thread::sleep_for(100 _ms); assert(f5.valid()); }
barrier barrier另一种基于条件变量提供的同步机制 当线程执行到barrier必须等待,直到所有的线程都达到这个点才能继续执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void case6 () { boost::atomic<int > x; barrier br (5 ) ; auto func = [&](){ cout << "thread" << ++x <<" arrived barrier." << endl ; br.wait(); cout << "thread run." << endl ; }; thread_group tg; for (int i = 0 ;i < 5 ;++i) { tg.create_thread(func); } tg.join_all(); }
thread_local c++11引入新关键字 thread_local,而thread库使用thread_specific_ptr实现了可移植的线程本地存储机制
使得这一的变量用起来像是每个线程独立拥有的,简化多线程应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void case7 () { thread_specific_ptr<int > pi; auto func = [&]{ pi.reset(new int ()); ++(*pi); cout << "thread v=" << *pi << endl ; }; async(func); async(func); this_thread::sleep_for(100 _ms); }
at_thread_exit this_thread在线程结束的时候执行可调用函数,无论线程是否被中断
1 2 3 4 5 6 7 void end_msg (const string & msg) { cout << msg << endl ; } void printing () { at_thread_exit(bind(end_msg, "end" )) }
asio asio库基于操作系统提供的异步机制,采用前摄器(Proactor)设模式实现了可移植的异步IO操作 而且并不要求使用多线程和锁,有效避免了多线程编程带来的诸多副作用(条件竞争、死锁等)
asio基于前摄器模式,封装了操作系统的select、kqueue、poll/epoll、overlapped I/O等机制, 实现了异步IO模型,它的核心类是io_service,相当于前摄器模式中的Proactor角色, asio的任何操作都需要io_service的参与
同步模式
程序发起一个IO操作,向io_service提交请求, io_service把操作转交给操作系统,同步等待 当IO操作完成时,操作系统通知io_service,然后io_service再把结果发回程序 完成整个同步流程与多线程的join类似
异步模式
程序除了要发起IO操作,还要定义一个回调的完成处理函数,io_service同样把io操作转交给操作系统执行 但他不同步等待而是立即返回,调用io_service的run()成员函数可以等待异步操作完成 当异步操作完成时候io_service从操作系统获取结果,再调用handler执行后续的逻辑
简介 handler handler是asio库重要概念,符合某种函数签名的回调函数,handler必须可拷贝,io_service会存储handler的拷贝,当异步时间发送io_service回调用事件对应的handler
handler不一定是函数和函数指针也可以是函数对象、function对象、bind/lambda表达式等
可以使用bind把任意函数适配成asio要求的handler形式
boost::asio::placeholders定义了几个占位符
io_service
io_service类代表了系统里的异步处理机制(如epoll),必须在asio库里的其他对象之前初始化
其他对象则向io_service提交一步操作handler
run()启动时间循环,阻塞等待所有注册到io_service的事件完成
strand asio库基于操作系统的异步IO模型,不直接使用系统线程,而是定义了一个自己的线程概念:strand
它序列化异步操作,保证异步代码在多线程的环境正确地执行,无需使用互斥量
wrap(),包装一个函数,返回一个相同签名的函数对象,保证线程安全地在strand中执行
strand可以理解对一组handler加了锁,这组handler不会存在线程并发访问的问题
work 当io_service里注册的事件完成时它就退出时间循环,有的时候希望io_service继续运行,处理将来可能发生的异步时间 这时候需要让io_service始终有事可做
work就是做这样的目的
构造函数启动一个可用的任务,析构函数停止任务,像一个guard,于是在work生命周期李io_service就永远不会因其他异步事件完成而结束事件循环
mutable_buffer和const_buffer IO操作经常会使用到数据缓冲区,相当于一片指定的内存区域
asio提供了两个mutable_buffer和const_buffer
为适配容器概念,还提供了begin和end操作
工厂函数
buffer()工厂函数产生buffer对象,包装常用C++容器类型,如原始数组、array、vector、string等
返回mutable_buffers_1或const_buffers_1
asio还提供了几个自由函数操作buffer
unix信号 UNIX信号是常用的进程间异步通信手段
asio库提供了signal_set,利用异步IO地方式很好处理unix信号
signal_set构造函数需要传入io_service对象,用于提交异步操作,第二种形式的构造函数还可以传入三个整数信号值,在构造的同时加入信号集
add/remove/clear成员函数理解为添加、删除信号量,同时也向io_service注册了信号事件,cancel取消了所有handler的执行,实际上是向他们传入boost::asio::error::operation_aborted错误,要求handler执行自己的cancel逻辑
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <boost/function.hpp> #include <boost/asio.hpp> using namespace boost::asio;using namespace boost::system;using boost::function;int main () { io_service io; signal_set sig (io, SIGINT, SIGUSR1) ; cout << "add:" << SIGINT << "," << SIGUSR1 << endl ; auto handler1 = [&](const error_code& ec, int n) { if (ec) { cout << ec.message() << endl ; return ; } if (n != SIGINT) { return ; } cout << "handler1 recv = " << n << endl ; cout << "do something" << endl ; }; typedef void (handler_type) (const error_code&, int ) ; function<handler_type> handler2 = [&](const error_code& ec, int n) { if (n != SIGUSR1) { return ; } cout << "handler2 recv = " << n << endl ; sig.async_wait(handler1); sig.async_wait(handler2); }; sig.async_wait(handler1); sig.async_wait(handler2); io.run(); cout << "io stoped" << endl ; }
signal_set捕获sigint和sigusr1,并使用lambda定义了对应的两个处理函数
async_wait通知io_service异步地执行IO操作,并且注册了handler回调函数
run,启动前摄器事件处理循环,否则程序会因为不等待事件发生,进入阻塞状态,等待信号事件并分派事件,直至所有操作完成然后退出run
1 2 kill -30 pid handler2 recv = 30
程序捕获了信号SIGUSR1后执行handler,如果想让程序持续捕捉信号,只在收到SIGINT时才退出则信号处理完毕后重新添加handler
定时器 asio库,提供了deadline_timer,steady_timer,system_timer和high_resolution_timer四个定时器
定时器非常重要的功能,指定某个时刻调用函数,实现异步操作
deadline_timer是asio早期版本提供的定时器,使用boost.date_time库提供时间才支持
而后三个定时器则使用std::chrono或者boost::chrono里的时钟类提供时间支持
但是后三个不在boost/asio.hpp,而在额外的头文件
basic_waitable_timer
steady_timer,system_timer和high_resolution_time是basic_waitable_timer的模板特化typedef
定时器的三种形式构造函数,同signal_set一样要求有io_service对象,用于提交io请求,第二个参数是定时器的终止时间,可以是绝对时间点和相对当前时间长度
一旦定时器创建就开始计时
成员函数wait来同步等待定时器终止,或者async_wait异步等待,当定时器终止会调用handler函数
成员函数expires_at或expires_from_now分别设置定时器终止的时间点和相对时间,然后wait或async_wait等待
成员函数calcen,传递error::operation_aborted错误码通知所有异步操作取消,返回已经取消的handler数量
cancel_one功能如同cancel,但它一次只取消一个handler
async_wait异步等待的handler形式要求是
1 void handler (const error_code& ec) ;
同步定时器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <boost/chrono.hpp> using namespace boost::chrono;seconds operator "" _s(unsigned long long n) { return seconds(n); } milliseconds operator "" _ms(unsigned long long n) { return milliseconds(n); } #include <boost/bind.hpp> #include <boost/function.hpp> #include <boost/asio.hpp> #include <boost/asio/steady_timer.hpp> void case1 () { io_service io; steady_timer t (io, 500 _ms) ; cout << t.expires_at() << endl ; cout << t.expires_from_now() << endl ; t.wait(); cout << "hello asio1" << endl ; }
与thread的sleep对比,两者都是等待,但内部机制不一样,thread的sleep使用了互斥量和条件变量 在线程中等待,而asio则调用了操作系统的一步机制如select、epoll完成没有多线程竞争
异步定时器 1 2 3 4 5 6 7 8 9 10 11 12 13 void case2 () { io_service io; steady_timer t (io, 500 _ms) ; t.async_wait( [](const error_code& ec) { cout << "hello asio2" << endl ; }); io.run(); }
这样当定时器到期时,io_service会回调处理函数,完成异步操作
案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 class timer_with_func { typedef timer_with_func this_type; private : int m_count = 0 ; int m_count_max = 0 ; function<void ()> m_f; steady_timer m_t ; public : template <typename F> timer_with_func(io_service& io, int x, F func): m_count_max(x), m_f(func), m_t (io, 200 _ms) { init(); } private : typedef void (handler_type) (const error_code&) ; function<handler_type> m_handler = [&](const error_code&) { if (m_count++ >= m_count_max) { return ; } m_f(); m_t .expires_from_now(200 _ms); m_t .async_wait(m_handler); }; void init () { m_t .async_wait(m_handler); } void handler (const error_code&) { if (m_count++ >= m_count_max) { return ; } m_f(); m_t .expires_from_now(200 _ms); m_t .async_wait(bind( &this_type::handler, this , boost::asio::placeholders::error)); } }; void case3 () { io_service io; timer_with_func t1 (io, 5 , []{cout << "hello timer1" <<endl ;}) ; io.run(); }
通信概述 asio库支持TCP、UDP、ICMP通信协议,它的名字空间boost::asio:ip里提供了大量的网络通信方面的函数和类 很好封装了伯克利SocketAPI
ip::tcp是asio网络通信部分主要的类,表示TCP协议 但它本身并没有太多功能,而是定义了数个用于TCP通信的typedef类型,用来协作完成网络通信
typedef:包括端点类endpoint、套接字类socket、流类iostream、接收器acceptor、解析器resolver等
ip::tcp更像一个名字空间
ip::tcp内部类型endpoint、socket、acceptor和resolver是asio库TCP通信中最核心的一组类
封装了socket的链接、断开和数据收发等功能,使他们可以容易编写出socket程序
address IP地址独立于TCP、UDP等通信协议
asio库使用ip::address表示ip地址,可以同时支持ipv4和ipv6两种地址
工厂函数
address静态成员函数from_string(),可以从字符串产生IP地址
is_v4(),is_v6()可以用来检测地址的版本
to_string()函数可以将ip地址转换成字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <boost/function.hpp> #include <boost/asio.hpp> using namespace boost::asio;using namespace boost::system;void case1 () { ip::address addr; addr = addr.from_string("127.0.0.1" ); assert(addr.is_v4()); cout << addr.to_string() << endl ; addr = addr.from_string("ab::12:34:56" ); assert(addr.is_v6()); }
endpoint 有了ip地址,再加上通信的端口号就构成了一个socket端点,在asio库中用了ip::tcp::endpoint表示
1 2 3 4 5 6 7 8 void case2 () { ip::address addr; addr = addr.from_string("127.0.0.1" ); ip::tcp::endpoint ep (addr, 6688 ) ; assert(ep.address() == addr); assert(ep.port() == 6688 ); }
socket socket类是tcp通信的基本类,basic_stream_socket的tcp协议特化
socket构造时指定使用的协议和endpoint,或者稍后调用connect()
连接成功后可以用local_endpoint()和remote_endpoint()获得连接两端的端点信息
available()获取可读取的字节数
receive()/read_some()和send()/write_some()读写数据
close()函数关闭socket,如果不关闭socket,那么在socket对象析构也会自动调用close()关闭
1 2 3 send()/receive()和wirte_some()/read_some()函数功能完全相同,只是名字不同 内部调用的是系统函数::sendmsg()和::recvmsg() 但send()/receive()函数要多出一种使用socket_base::message_flags参数的重载形式
socket读写函数都是buffer类型,buffer()函数包装各种容器适配
send、wiite_some需要可读的buffer
receive、read_some需要可写的buffer
acceptor acceptor类对应Socket Api的accept()函数功能
它用于服务器端,在指定的端口接收连接,必须配合socket类才能完成通信
acceptor可以像传统socket api,open()打开端口,bind()端口绑定,listen()监听端口
更方便是使用它的构造函数, 传入endpoint直接完成三个动作
在开始监听之后,调用accept()就可以接收新的连接,连接成功的socket在函数参数里以引用的形式输出
同步tcp通信 服务端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 int main () try { typedef ip::tcp::acceptor acceptor_type; typedef ip::tcp::endpoint endpoint_type; typedef ip::tcp::socket socket_type; cout << "server start." << endl ; io_service io; acceptor_type acceptor (io, endpoint_type(ip::tcp::v4(), 6688)); //一套完成bind,listen cout << acceptor.local_endpoint().address() << endl ; for (;;) { socket_type sock (io) ; acceptor.accept(sock); cout << "client:" ; cout << sock.remote_endpoint().address() << endl ; sock.send(buffer("hello asio" )); } } catch (std ::exception& e){ cout << e.what() << endl ; }
客户端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <boost/function.hpp> #include <boost/asio.hpp> using namespace boost::asio;using namespace boost::system;int main () try { typedef ip::tcp::endpoint endpoint_type; typedef ip::tcp::socket socket_type; typedef ip::address address_type; cout << "client start." << endl ; io_service io; socket_type sock (io) ; endpoint_type ep(address_type::from_string("127.0.0.1"), 6688); sock.connect(ep); cout << sock.available() << endl ; vector <char > str(5 ,0 ); error_code ec; for (;;) { sock.read_some(buffer(str), ec); if (ec) { break ; } cout << &str[0 ]; } cout << endl ; } catch (std ::exception& e){ cout << e.what() << endl ; }
异步tcp通信 原有的同步调用函数换成前缀async_的异步调用函数,并增加回调函数
回调函数中在启动一个异步调用确保io_service继续处理socket事件
服务端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 #include <boost/smart_ptr.hpp> #include <boost/bind.hpp> #include <boost/asio.hpp> using namespace boost;using namespace boost::asio;using namespace boost::system;class server { typedef server this_type; typedef ip::tcp::acceptor acceptor_type; typedef ip::tcp::endpoint endpoint_type; typedef ip::tcp::socket socket_type; typedef boost::shared_ptr <socket_type> sock_ptr; private : io_service m_io; acceptor_type m_acceptor; public : server(): m_acceptor(m_io,endpoint_type(ip::tcp::v4(), 6688 )) { accept(); } void run () { m_io.run(); } private : void accept () { sock_ptr sock(new socket_type(m_io)); m_acceptor.async_accept(*sock, [this , sock](const error_code& ec) { if (ec) { return ; } sock->async_send( buffer("hello asio" ), [](const error_code& ec, std ::size_t ) { cout << "send msg complete." << endl ; } ); accept(); } ); } void accept_handler (const error_code& ec, sock_ptr sock) { if (ec) { return ; } cout << "client:" ; cout << sock->remote_endpoint().address() << endl ; sock->async_write_some(buffer("hello asio" ), bind(&this_type::write_handler2, this , boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); accept(); } void write_handler (const error_code&) { cout << "send msg complete." << endl ; } void write_handler2 (const error_code&, std ::size_t n) { cout << "send msg " << n << endl ; }}; int main () { server svr; svr.run(); }
客户端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 using std ::cout ;using std ::endl ;using std ::vector ;#define BOOST_ASIO_DISABLE_STD_CHRONO #include <boost/bind.hpp> #include <boost/function.hpp> #include <boost/asio.hpp> using namespace boost::asio;using namespace boost::system;using boost::shared_ptr ;class client { typedef client this_type; typedef ip::tcp::endpoint endpoint_type; typedef ip::address address_type; typedef ip::tcp::socket socket_type; typedef shared_ptr <socket_type> sock_ptr; typedef vector <char > buffer_type; private : io_service m_io; buffer_type m_buf; endpoint_type m_ep; public : client(): m_buf(5 ,0 ), m_ep(address_type::from_string("127.0.0.1" ), 6688 ) { start(); } void run () { m_io.run(); } void start () { sock_ptr sock(new socket_type (m_io)); //创建socket对象 sock->async_connect(m_ep, [this , sock](const error_code& ec) { if (ec) { return ; } sock->async_read_some(buffer(m_buf), [this , sock](const error_code& ec,std ::size_t ) { read_handler(ec, sock); } ); } ); } void conn_handler (const error_code& ec, sock_ptr sock) { if (ec) { return ; } cout << "recive from " << sock->remote_endpoint().address(); sock->async_read_some(buffer(m_buf), bind(&client::read_handler, this , boost::asio::placeholders::error, sock)); } void read_handler (const error_code& ec, sock_ptr sock) { if (ec) { return ; } cout << &m_buf[0 ] << endl ; sock->async_read_some(buffer(m_buf), bind(&client::read_handler, this , boost::asio::placeholders::error, sock)); } }; int main () { cout << "client start." << endl ; client cl; cl.run(); }
解析网络地址 resolver类对用socketApi的getaddrinfo()函数,用于解析网址获得可用的ip地址
解析得到的ip地址可以使用socket对象连接
resolver类通过域名获得可用的ip,实现与ip版本无关的网址解析
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <boost/function.hpp> #include <boost/asio.hpp> using namespace boost::asio;using namespace boost::system;#include <boost/chrono.hpp> using namespace boost::chrono;seconds operator "" _s(unsigned long long n) { return seconds(n); } milliseconds operator "" _ms(unsigned long long n) { return milliseconds(n); } void resolve_connect (ip::tcp::socket &sock, const char * name, int port) { ip::tcp::resolver r(sock.get_io_service()); //创建一个resolver对象 ip::tcp::resolver::query q(name, boost::lexical_cast<string>(port)); auto iter = r.resolve(q); decltype (iter) end; error_code ec = error::host_not_found; for ( ;ec && iter != end; ++iter) { sock.close(); sock.connect(*iter,ec); } if (ec) { cout << "can't connect." << endl ; throw system_error(ec); } cout << "connect success." << endl ; } void case1 () try { io_service io; ip::tcp::socket sock (io) ; resolve_connect(sock,"www.boost.org" , 80 ); cout << sock.remote_endpoint() << endl ; } catch (std ::exception& e){ cout << e.what() << endl ; };
resolve_connect()函数中使用了lexical_cast,这是因为query对象只接受字符串参数,所以需要把端口号由整型转换成字符串
协程 协程是泛化的例程,例程只有一个入口和多个出口
函数入口开始,可以在某个时刻用return返回,例程就结束了,而协程不同,有多个入口个多个出口
最开始的入口进入之后可以随时用yield调用返回,之后再调用协程就会从刚才返回的地方继续执行
asio的协程功能主要使用类yield_context,它是basic_yield_context的typedef
yield_context的接口很简单,保存了协程的运行环境,交替执行主协程(caller)和从协程(callee),达到异步地目的
operator[]用于外部获取发生的错误码,如果不使用operator[]则会抛出system_error异常来报告错误
创建
无法直接创建yield_context对象,而是使用函数spawn(),它产生yield_context对象,
spawn()多个重载形式
boost::asio::spawn(my_strand, do_echo); 一般输入2个参数,参数1是 io_service 或者是 strand, 参数2是协程函数,类型如下: void coroutine(boost::asio::yield_context yield);
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <boost/function.hpp> #include <boost/asio.hpp> #include <boost/asio/spawn.hpp> using namespace boost::asio;using namespace boost::system;int main () { typedef ip::tcp::acceptor acceptor_type; typedef ip::tcp::endpoint endpoint_type; typedef ip::tcp::socket socket_type; io_service io; spawn(io, [&](yield_context yield) { acceptor_type acceptor(io, endpoint_type(ip::tcp::v4(), 6688 )); for (;;) { socket_type sock(io); error_code ec; acceptor.async_accept(sock, yield[ec]); if (ec) { return ; } auto len = sock.async_write_some( buffer("hello coroutine" ), yield); cout << "send " << len << " bytes" << endl ; } } ); io.run(); }
整个操作看起来像是同步的,没有放handle回调处理
在协程函数中调用各个异步IO,异步操作将挂起协程,待异步操作完成后会自动继续协程。
高阶工具 optional 经常遇到无效值的情况,或函数并不能总返回有效值,很多函数正确执行,但结果不合理
操作函数
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void case2 () { optional<int > op0; optional<int > op1(none); assert(!op0); assert(op0 == op1); assert(op1.value_or(253 ) == 253 ); cout << op1.value_or_eval( [](){return 874 ;}) << endl ; optional<string > ops("test" ); cout << *ops << endl ; ops.emplace("monado" , 3 ); assert(*ops == "mon" ); vector <int > v(10 ); optional<vector <int >& > opv(v); assert(opv); opv->push_back(5 ); assert(opv->size() == 11 ); opv = none; assert(!opv); }
工厂函数
1 2 3 4 5 6 7 8 void case4 () { auto x = make_optional(5 ); assert(*x == 5 ); auto y = make_optional<double >((*x > 10 ), 1.0 ); assert(!y); }
assign 为容器初始化或赋值,填入大量数据
1 2 #include <boost/assign.hpp> usng namespace boost:assign;
list_inserter list_inserter是assign库中用来操作容器的工具类,类似std::back_inserter,但增加了许多操作符重载和助手类简化代码
operator+= 由于list_inserter重载了+=操作符和逗号,可以用简洁的语法完成插入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> using namespace std ;#include <boost/assign.hpp> void case1 () { using namespace boost::assign; vector <int > v; v += 1 ,2 ,3 ,4 ,5 , 6 *6 ; for (auto & x : v) cout << x << "," ; cout << endl ; set <string > s; s += "c" , "cpp" , "lua" , "swift" ; for (auto & x : s) cout << x << "," ; cout << endl ; map <int , string > ms; ms += make_pair(1 , "one" ),make_pair(2 , "two" ); }
operator() operator +=仅用于标准容器,在处理map容器显得的麻烦,所以直接用工厂函数insert()/push_front()/push_back(),直接利用他们返回的list_inserter对象填入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <forward_list> void case2 () { using namespace boost::assign; vector <int > v; push_back(v)(1 )(2 )(3 )(4 )(5 ); list <string > l; push_front(l)("c" )("cpp" )("lua" )("swift" ); forward_list<string > fl; push_front(l)("matrix" )("reload" ); set <double > s; insert(s)(3.14 )(0.618 )(1.732 ); map <int , string > m; insert(m)(1 , "one" )(2 , "two" ); }
generic_list list_inserter解决了赋值问题,但是有时候需要在构造函数完成数据填充
这个时候c++11引入了std::initializer_list,而boost.assign库提供功能类似的generic_list
assign 库提供三个工厂函数list_of(),map_list_of()/pair_list_of()和tuple_list_of(), 能够产生generic_list对象,然后就可以像list_inserter一样使用operator()和operator来填充数据
和之前的insert(),push_back()等函数相似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void case4 () { using namespace boost::assign; vector <int > v = list_of(1 )(2 )(3 )(4 )(5 ); deque <string > d = (list_of("power" )("bomb" ),"phazon" ,"suit" ); set <int > s = (list_of(10 ), 20 ,30 ,40 ,50 ); map <int , string > m = list_of(make_pair(1 , "one" ))(make_pair(2 , "two" )); map <int , int > m1 = map_list_of(1 , 2 )(3 , 4 )(5 , 6 ); map <int , string > m2 = map_list_of(1 , "one" )(2 , "two" ); }
减少重复输入 list_inserter和generic_list都提供成员函数repeat()、repeat_fun()和range()减轻工作量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void case5 () { using namespace boost::assign; vector <int > v = list_of(1 ).repeat(3 , 2 )(3 )(4 )(5 ); for (auto & x : v) cout << x << "," ; cout << endl ; multiset <int > ms ; insert(ms).repeat_fun(5 , &rand).repeat(2 , 1 ), 10 ; for (auto & x : ms) cout << x << "," ; cout << endl ; deque <int > d; push_front(d).range(v.begin(), v.begin() + 5 ); for (auto & x : d) cout << x << "," ; cout << endl ; }
swap 交换两个变量(int等内置数据类型,或类实例、容器)的值提供了便捷方法
c++98的std::swap()
1 2 3 4 5 6 template <typename T>void swap (T& a, T& b) { T tmp (a) ; a = b; b = tmp; }
从代码上看出std::swap要求交换的对象是可拷贝构造和可拷贝赋值的 它提供的最通用同时也效率低,需要进行一次复制构造和两次赋值操作,如果交换对象打,运行代价大
C++11标准使用了转移语义,对std::swap()进行了优化,避免了拷贝的代价1 2 3 4 5 6 template <typename T>void swap (T&a, T&b) { T tmp = std ::move(a); a = std ::move(b); b = std ::move(tmp); }
但不是所有类都实现了自己的转移构造和赋值函数,所以自己写的类最好能够实现swap()来提供效率
解决方案有两种
boost::swap就利用了上面两个方案,查找有无针对类型T的std::swap()特化或通过ADL查找模板特化的swap(),如果有则调用
交换数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> using namespace std ;#include <boost/core/swap.hpp> #include <boost/assign.hpp> void case1 () { using namespace boost::assign; int a1[10 ]; int a2[10 ]; std ::fill_n(a1, 10 , 5 ); std ::fill_n(a2, 10 , 20 ); boost::swap(a1, a2); }
如果boost::swap交换两个长度不相同的数组,那么无法通过编译
特化std::swap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class point { int x, y, z; public : explicit point(int a=0, int b=0, int c=0):x(a),y(b),z(c){} void print () const { cout << x <<"," << y <<"," << z << endl ; } void swap (point &p) { std ::swap(x, p.x); std ::swap(y, p.y); std ::swap(z, p.z); cout << "inner swap" << endl ; } }; namespace boost {void swap (point &x, point &y) { x.swap(y);}} void case2 () { point a(1,2,3), b(4,5,6); cout << "std::swap" << endl ; std ::swap(a,b); cout << "boost::swap" << endl ; boost::swap(a, b); }
singleton 单例模式
singoleton把模板参数T实现为一个单例,对类型T要求是有缺省构造函数,而且析构和构造都不能抛出异常
用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> using namespace std ;#include <boost/serialization/singleton.hpp> using boost::serialization::singleton; class point : public singleton<point> { int x, y, z; public : point(int a=0 , int b=0 , int c=0 ):x(a),y(b),z(c) { cout << "point ctor" << endl ;} ~point() { cout << "point dtor" << endl ;} void print () const { cout << x <<"," << y <<"," << z << endl ; }}; typedef singleton<point> origin; int main () { cout << "main() start" << endl ; origin::get_const_instance().print(); origin::get_mutable_instance().print(); point::get_const_instance().print(); point::get_mutable_instance().print(); cout << "main() finish" << endl ; }
国内查看评论需要代理~