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;
}

//1000000
//max timespan:5.1241e+09h
//min timespan:1e-06s
//now time elapsed:0.000117s

使用了标注库头<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();
}

//0.00 s
//0.00 s
//0.00 s

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(),cout ,"%%%", "+++", "???");
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 ,"%%%", "+++", "???");

//%%%0% 10 20 30 40 50 60 70 80 90 100%
//+++|----|----|----|----|----|----|----|----|----|----|
//???***************************************************

注意事项

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;

//#define DATE_TIME_NO_DEFAULT_CONSTRUCTOR
#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); //date支持比较操作
assert(d3 < d4);
}

day_lock天级别的始终,工厂类调用了静态成员函数local_dayuniversal_day返回一个当天的日期对象,分别表示本地日期和UTC日期,day_lock内部使用了C标准库的localtimegmttime函数,因此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); //最大 9999-12-31
date d5(min_date_time);// 最小 1400-01-01

cout << d1 << d2 << d3 << d4 << d5 << endl;

try
{
//date d1(1399,12,1); //超过日期下限
//date d2(10000,1,1); //超过日期上限
date d3(2017,2,29); //不存在的日期
}
catch(std::exception& e)
{
cout << e.what() << endl;
}
}

//-infinity
//+infinity
//not-a-date-time
//9999-Dec-31
//1400-Jan-01
//Day of month is not valid for year

访日日期

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; //转换为YYYY-mmm-dd格式字符串,mmm为3字符英文字符串
cout << to_iso_string(d) << endl;
//输出YYYYMMDD格式的数字字符串
cout << to_iso_extended_string(d) << endl;
//转换为YYYY-MM-DD格式的数字字符串
cout << d << endl;

//cout << "input date:";
//cin >>d;
//cout << d;

}

日期长度

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); //3个星期
assert(w.days() == 21);

months m(5); //5个月
years y(2); //2年

months m2 = y + m; //2年5个月
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; //5435天
assert(d1 + (d2 - d1) == d2);

d1 += days(10); //2000-1-11
assert(d1.day() == 11);
d1 += months(2); //2000-3-11
assert(d1.month() == 3 && d1.day() == 11);
d1 -= weeks(1); //2000-03-04
assert(d1.day() == 4);

d2 -= years(10);//2004-11-18
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); //2017-2-28
d -= months(1); //2014-1-31
d += months(2); //2014-3-31
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));
//1-1至1-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));
//1-5至1-15
assert(dp.contains(dp2));

assert(dp.intersects(dp2));
assert(dp.intersection(dp2) == dp2);

date_period dp3(date(2010,1,21), days(5));
//1-21 到 1-26
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); //增减步长默认为1天

assert(d_iter == d);
++d_iter; //增长一天
assert(d_iter == date(2007,9,29));

year_iterator y_iter(*d_iter, 10); //增减步长为 10年
assert(y_iter == d + days(1));
++y_iter; //递增10年
assert(y_iter->year() == 2017);

day_iterator iter(day_clock::local_day());
++iter;

//iter += 5;
//std::advance(iter, 5);
}

时间

操作时间长度

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);//1小时10分30秒1毫秒
time_duration td2(1,60,60,1000*1000* 6 + 1000);//2小时01分06.001秒
}

hours h(1); //1小时
minutes m(10);//10分钟
seconds s(30);//30秒钟
millisec ms(1);//1毫秒

time_duration td = h + m + s + ms;//可以赋值给time_duration
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")); //构造scoped_ptr对象

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); //一个int指针的scoped_ptr

if (p) //在bool语境中测试指针是否有效
{
*p = 100;
cout << *p << endl;
}

p.reset(); // 置空scoped_ptr

assert(p == 0); //确实p不持有任何指针
if (!p)
{ cout << "scoped_ptr == nullptr" << endl; }

} //这里发生scoped_ptr的析构,p和fp管理的指针自动删除

unique_ptr区别

std::unique_ptr是c++11定义的新智能指针,用来取代c++98中的std::auto_ptr
std::unique_ptr不仅能代理new创建的单个对象还能代理new[]创建的数组对象
也就是它结合了scoped_arrayscoped_ptr两者的能力

std::unique_ptr基本能力和scoped_ptr相同,同样作用于管理指针,也不允许拷贝构造和拷贝赋值

std::unique_ptrscoped_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;
//a[5] = 1000;
}

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); //bool语境
*spi = 253;

shared_ptr<std::string> sps(new std::string("smart"));
assert(sps->size() == 5);

//shared_ptr<int> dont_do_this(new int[10]);
}

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); //引用计数为2,两个shared_ptr相等

*sp2 = 100; //解引用操作符修改被指对象
assert(*sp == 100); //另一个也同时修改

sp.reset(); //停止shared_ptr使用
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();
}
//输出
//count: 3 v=100
//count: 3 v=100
//count: 4 v=20
//count: 3 v=20

工厂函数

为了去除过多显式的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; //shared_ptr成员变量
public:
sample(); //构造函数
void print(); //提供给外界的接口
};

class sample::impl //内部实现类
{
public:
void print()
{ std::cout << "impl print" << std::endl;}
};

sample::sample():p(new impl){} //构造函数初始化shared_ptr

void sample::print()
{ p->print();}

void case1()
{
sample s;
s.print();//调用pimpl实现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 shared_ptr<abstract>(new impl);}
{ return make_shared<impl>();}

void case2()
{
auto p = create();
p->f();
p->g();

abstract *q = p.get();
boost::ignore_unused(q);
//delete q; //错误 abstract的保护析构函数
//impl *q = (impl*)(p.get());//强制转 粗鲁的方法
//delete q; ok
}

删除容器

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() //打开socket
{
std::cout << "open_socket" << std::endl;
return new socket_t;
}

void close_socket(socket_t * s) //关闭socket
{
std::cout << "close_socket" << std::endl;
}

void case3()
{
socket_t *s = open_socket();
shared_ptr<socket_t> p(s, close_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); //shared_array 代理动态数组
assert(sa.unique()); //唯一持有指针

shared_array<int> sa2 = sa; //共享数组,引用计数增加
assert(sa2.use_count() == 2);

sa[0] = 10; //可以通过operator[]访问元素
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>
//using namespace std;

#include <boost/smart_ptr.hpp>
using namespace boost;

//////////////////////////////////////////

void case1()
{
shared_ptr<int> sp(new int(10)); //一个shared_ptr
assert(sp.use_count() == 1);

weak_ptr<int> wp(sp); //从shared_ptr创建weak_ptr
assert(wp.use_count() == 1); //weak_ptr不影响引用计数

if (!wp.expired()) //判断weak_ptr观察的对象是否失效
{
shared_ptr<int> sp2 = wp.lock(); //获得一个shared_ptr
*sp2 = 100;
assert(wp.use_count() == 2);
} //退出 sp2析构 引用计数减1

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); //获取shared_ptr
assert(px.use_count() == 2); //引用计数为2

auto p = new raw_shared; //原始指针

auto wp = weak_from_raw(p); //weak_ptr
assert(wp.use_count() == 1); /此时无引用 0

decltype(shared_from_raw(p)) spx(p); //获取shared_ptr

auto sp = shared_from_raw(p); //
//std::cout << sp.use_count() << std::endl;
assert(sp.use_count() == 2);

//decltype(sp) spx(p);

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;
//typedef shared_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(); //调用lock获得强引用
}
} //退出作用于,shared_ptr均正常析构

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); //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>

//#define BOOST_SYSTEM_NO_DEPRECATED
//#define BOOST_POOL_NO_MT
#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"); //字符串->float
double e = lexical_cast<double>("2.71828"); //字符串->double
double r = lexical_cast<double>("1.414,xyz", 5); //C字符串->double

cout << x << y << pai << e << r << endl;

string str = lexical_cast<string>(456); //整数->字符串
cout << str << endl;

cout << lexical_cast<string>(0.618) << endl; //float->字符串
cout << lexical_cast<string>(0x10) << endl; //16进制整型->字符串

cout << lexical_cast<bool>("1") << endl;

//lexical_cast<int>("0x100");
//lexical_cast<int>("123L");
}


//10020003141592.718281.414
//456
//0.61799999999999999
//16

异常处理

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标准增强了字符串与数字的互操作性,提供了

  • stoX()函数
  • to_string()函数

实现了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(776ul) == "776");

//cout << stoul("100L");
//cout << stoul("x100");
cout << stoi("9999999999");
}
catch(std::exception& e)
{
cout << "error:" << e.what() << endl;
}

format

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();
}

//sum:1+2=3
//(2 + 5) * 5 = 35

通过操作符% 逐个”喂”给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); //将第二个输入参数固定位数字10
cout << fmt %1 %3;

fmt.clear();

cout << fmt % group(showbase,oct, 111) % 333; //%操作符使用group(),指定输入/输出流操作纵符第一个参数显示为八进制

fmt.clear_binds();


fmt.modify_item(1, group(hex, right, showbase,setw(8), setfill('*')));
cout << fmt % 49 % 20 % 100;
}


//1 2 3 2 1
//1 10 3 10 1
//0157 10 333 10 0157
//****0x31 20 100 20 49

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);

//boost::ignore_unused(s4, s5);


//const char* ch = "Apple iPhone iPad";

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));

//cout << (to_upper_copy(str1) == to_upper_copy(str2)) << endl;
//cout << is_iequal()(str1, str2) << endl;
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, "**", is_contains_a());
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");

//typedef find_iterator<string::iterator> string_find_iterator;

//string_find_iterator pos, end;
//for (pos = make_find_iterator(str, first_finder("samus", is_iequal()));
// pos != end; ++pos)
//{ cout << "[" << *pos << "]" ; }

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>
//using namespace std;

#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()
{
//using namespace boost::random;
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));
//for (int i = 0; i < buf_len; ++i)
//{ buf[i] = 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>
//using namespace boost;

//////////////////////////////////////////

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 xx = boost::minmax(s1, s2); //因为内部std命名空间混乱了
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>
//using namespace boost;

//////////////////////////////////////////

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);
}

//3,4,6,8,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
2B2D2A2F
2b2d2a2f

容器和数据结构

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>
//using namespace std;

#include <boost/array.hpp>
using namespace boost;

//////////////////////////////////////////
void case1()
{
array<int, 10> ar; //一个大小为10的int数组

ar[0] = 1; //使用operator[]操作
ar.back() = 10; //back访问最后一个元素
assert(ar[ar.max_size() - 1] == 10);

ar.assign(777); //数组所有元素赋值为777
for (auto x : ar)// for auto 遍历
{ std::cout << x << ","; }

int *p = ar.c_array(); //获得原始数组指针
*(p + 5) = 253;
std::cout << ar[5] << std::endl;

ar.at(8) = 666; //使用at函数访问元素
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); //大小为10的dynamic_bitset
dynamic_bitset<> db3(0x16,
BOOST_BINARY(10101)); //注意这里 //大小为20的dynamic_bitset
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; //使用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
void case2()
{
dynamic_bitset<> db; //空的dynamic_bitset

db.resize(10, true); //扩展10个二进制,值全为1
cout << db << endl; //输出1111111111

db.resize(5); //缩小容量5
cout << db << endl; //输出11111

{
dynamic_bitset<> db(5,BOOST_BINARY(01110));

cout << db << endl; // 01110
assert(db.size() == 5); //目前有5个二进制位

db.clear(); //清空dynamic_bitset
assert(db.empty()&& db.size()==0);

}

assert(dynamic_bitset<>(64).num_blocks()==1); //64位linux占用一个区块
assert(dynamic_bitset<>(65).num_blocks()==2); //64位linux占用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; //0000000000000000000000000000010101001

}
}

位运算与比较运算

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) //for遍历集合
{ 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); //assign库赋值
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"); //assign初始化

um.insert(make_pair(10,"ten")); //insert函数
cout << um[10] << endl; //operator[]访问元素
um[11] = "eleven"; //关联数组用法
um[15] = "fifteen";

auto p = um.begin();
for (; p != um.end(); ++p) //使用auto获得迭代器
{ cout << p->first << "-" << p->second << ","; }
cout << endl;

um.erase(11); //删除键值为11的元素
cout << um.size() << endl; //输出5

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, //分段构造pair
make_tuple(1),make_tuple(1.0, 2.0)); //使用make_tuple
s.emplace(boost::unordered::piecewise_construct, //分段构造pair
make_tuple(3),make_tuple(3.0, 4.0)); //使用make_tuple

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.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)); //无效操作


//左视图map<int,string> 使用迭代器迭代
for (auto pos = bm.left.begin(); //auto
pos != bm.left.end();++pos)
{
cout << "left[" << pos->first << "]="
<< pos->second << endl;
}


//bm.right.begin()->second = 234;

{
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";

//bm.right.insert(make_pair("string", 10));

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"); //可以使用这类方式赋值
//bm_t bm = {{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); //左视图查找键值1
cout << "[" << pos->first
<< "]=" << pos->second << endl;
auto pos2 = bm.right.find("peach"); //右视图查找键值peach
cout << "[" << pos2->first
<< "]=" << pos2->second << endl;

//pos = bm.left.find(1);
//bm.left.replace_key(pos, 111);
//bm.left.replace_data(pos, "luigi");

pos = bm.left.find(1);
bm.left.modify_key(pos,_key = 111); //修改key为11
bm.left.modify_data(pos,_data = "luigi");

print_map(bm.left);
}



[1]=mario
[peach]=2


2<-->peach
111<-->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); //声明一个大小为5的循环缓冲区
assert(cb.empty()); //缓冲区目前无数据

cb.push_back(1); //向后端添加元素1
cb.push_front(2); //向前端添加元素1
assert(cb.front() == 2);
cb.insert(cb.begin(), 3); //向前端添加元素3


// 可以使用迭代器遍历容器
for (auto pos = cb.begin(); pos != cb.end();++pos)
{ cout << *pos << ","; } //输出3,2,1
cout << endl;

cb.pop_front(); //弹出首元素3
assert(cb.size() == 2);
cb.push_back(); //淡出末元素1
assert(cb[0] = 2);

//using namespace boost::assign;
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
{
for (auto& x: cb)
{ cout << x << ","; }
cout << endl;
}

void case2()
{
//assign库初始化
circular_buffer<int> cb = (assign::list_of(1),2,3);
print(cb); //1,2,3 此时缓冲区已满

cb.push_back(4); //4将覆盖最开始的1
print(cb); //2,3,4, begin()从2开始

cb.push_back(5); //5将覆盖最开始的2
print(cb); //3,4,5 begin()从3开始

cb.pop_front(); //弹出最开始的3
print(cb); //4,5 现在circular_buffer只有两个元素
}

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); // 3,4,5,1,2
}

空间优化型缓冲区

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; //3-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); //取第一个元素 int类型
assert(t.get<2>() == 100.0); //取第三个元素,double类型

std::cout << t.get<1>(); //第二个元素 const char类型
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; //3-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 << "please input tuple:";
//std::cin >> t1;

//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);
}


//make_ptr_any 工厂函数,封装了any使用shared_ptr的用法
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); //int
v.push_back(1.414); //double
v.push_back(boost::shared_ptr<int>(new int(100) )); //shared_ptr

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; //可容纳int,float和string
v = "123"; //v持有一个string对象

cout << v << endl; //无需cast
}

用法

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->int
v = 2.13; //v->double
assert(v.type() == typeid(double));

var_t v2("string type"); //v2->string
cout << get<string>(v2);

v2 = v; //v2->double

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; //if-elase分支语句检测类型
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);

//using namespace boost::assign;
//v = vector<int>({1,2});
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;

//int ma[x][y][z];

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()
{
//声明一个三维数组,维度2/3/4
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;
}

//cout << ma[2][3][4]; //引发越界错误

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};

//把[2][3][4]变为[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;
//indices[range(0,2)][range(0,2)];

auto view = ma[indices[range(0,2)][range(0,2)] ]; //抽取2*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; //2

}


//4
//0,0,
//0,0,
//2

适配普通数组

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; }


//是配成3*4二维数组
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;
}


//适配2*6数组
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>
<!-- conf comment -->
<gui>1</gui>
<theme id="001">matrix</theme>
<urls>
<!-- urls comment -->
<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; //声明一个ptree对象
read_xml("conf.xml", pt); //读取xml配置信息

pt.get<string>("conf.theme"); //获取conf.theme节点信息
pt.get<int>("conf.clock_style"); //获取conf.clock_style的int值
pt.get("conf.no_prop", 100); //获取config.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://www.url1.com,http://www.url2.com,http://www.url3.com,
urls comment ,http://www.url1.com,http://www.url2.com,http://www.url3.com,

写入配置信息

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.put("conf.urls.url", "http://www.url4.org");
pt.add("conf.urls.url", "http://www.url4.org");

write_xml(cout , pt); //向标准输出流写入数据
}

其他用法

find寻找某节点值

1
2
3
4
5
6
7
8
//<a>string</a>
//<b>100</b>
//<c>false</c>

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());//显式调用load取值

b.store(true); //显式调用store存值
assert(b);

atomic<int> n(100);
assert(n.exchange(200) == 100); //存值的同时返回原值
assert(n == 200);//隐式类型转换、等价于load
}

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; //设置变量expected 左值
if (l.compare_exchange_weak(v, 313)) //比较并交换
{
assert(l == 313 && v == 100); //如果成功值改变,输出原值100
}

v = 200; //设置expected=200
auto b = l.compare_exchange_strong(v, 99); //比较并减缓
assert(!b && v == 313); //交换失败并输出原值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); //相当于fetch_sub,返回运算后值

atomic<int> b{BOOST_BINARY(1101)}; //二进制1101

auto x = b.fetch_and(BOOST_BINARY(0110)); //逻辑与运算 返回原值1101
assert(x == BOOST_BINARY(1101) &&
b == BOOST_BINARY(0100)); //b运算后是0100
assert((b |= BOOST_BINARY(1001)) //相当于fetch_or,返回运算后值
== BOOST_BINARY(1101)); //b运算后是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; //atomic类型
mutable atomic_type m_count{0}; //mutable类型
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()
{
//demo::counted_ptr p(new demo); //创建智能指针
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 (...) //必须使用try-catch块保证解锁互斥量
{
mu.unlock();
}

{
lock_guard<mutex> g(mu); //函数栈退出撤销mutex
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); //等待100毫秒
if(flag) //检查是否成功锁定互斥量
{
cout << "lock timed mutex" << endl; //访问共享资源
mu.unlock(); //解锁互斥量
}

{
if(mu.try_lock_for(100_ms)) //等到100毫秒
{
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); // lock_guard
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;
//typedef unique_lock<timed_mutex> lock_type;

{
//lock_type g(tm, 100_ms);
auto g = my_make_lock(tm, 100_ms); //限时100毫秒锁定
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> //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); //无需其他mutex,自身可lock
a.deposit(100);
a.withdraw(20);
assert(a.sum() == 80);
}

{
auto b = make_unique_lock(a, try_to_lock); //可以try_lock
if(b)
{
a.withdraw(a.sum()); //取出全部存款
assert(a.sum() == 0);
}
}
}

lockable概念检查类

检查类型是否可锁定

lock函数

除了使用mutex的成员函数或者lock_guard/unique_lock,我们还可以使用两个自由函数

  • lock
  • try_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); //使用adopt_lock
auto g2 = make_unique_lock(m2, adopt_lock);

lock(m1, m2); //锁定两个mutex
}

{
auto g1 = make_unique_lock(m1, defer_lock);//使用defer_lock
auto g2 = make_unique_lock(m2, defer_lock);

try_lock(g1, g2); //锁定两个unique_lock
} //unique_lock自动解锁
}

使用线程

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); //等待200毫秒

}

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);}); //使用lambda表达式启动线程

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)
{
//this_thread::sleep_for(400_ms);
cout << i << endl;
this_thread::interruption_point();
}
}
catch(const thread_interrupted& )
{
cout << "thread_interrupted" << endl; //捕获中断异常
}

void case5()
{
thread t(to_interrupt,10);
//this_thread::sleep_for(1_s);

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; //打开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(); //可被中断
} //离开作用于,di/ri都被析构
//恢复现场最初的可中断状态
assert(interruption_enabled()); //此时允许中断
}
catch(const thread_interrupted& )
{
cout << "[thread_interrupted]" << endl;
}

void case6()
{
thread t(to_interrupt2,10);
//this_thread::sleep_for(1_s);

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)); //使用bind创建线程
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;}); //条件变量等待
//for(;is_full();)
//{
// cout << "full waiting... " << endl;
// cond_put.wait(lock);
//}
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;}); //条件变量等待
//for(;is_empty();)
//{
// cout << "empty waiting... " << endl;
// cond_get.wait(lock);
//}
--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_guard<shared_mutex> g(rw_mu);
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); //计算fab(5),时机不确定
auto f7 = async(launch::async, fab, 7); //要求立即开始计算fab(7)

cout << f5.get() + f7.get() << endl; //要求计算出future值
assert(!f5.valid() && !f7.valid()); //此时future无效

auto f10 = async(fab, 10); //计算fab(10)
auto s = f10.wait_for(100_ms); //future等到100毫秒计算结果

if(f10.valid()) //已经完成计算
{
assert(s == future_status::ready); //future
cout << f10.get() << endl; //计算结果89
}



//多个future

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]); //等待任意一个future值
for(auto& x : vec)
{
if(x.valid())
{ cout << x.get() << endl; }
}

//wait_for_all(vec.begin(), vec.end()); //等待所有计算结束

//for(auto& x : vec)
//{
// cout << x.get() << ',';
//}
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()
{
//shared_future<int> f5 = async(fab, 5);
auto f5 = async(fab, 5).share();
//cout << f5.get() << endl;

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;

// kill -10 xxxx

int main()
{
io_service io; //io_service对象
//io_service::work w(io);

signal_set sig(io, SIGINT, SIGUSR1); //捕获两个信号
cout << "add:" << SIGINT << "," << SIGUSR1 << endl;

//sig.add(SIGINT); //等价与signal_set构造函数
//sig.add(SIGUSR1);

auto handler1 = //定义一个handler,使用lambda
[&](const error_code& ec, int n) //函数签名必须符合要求
{
//cout << "enter sigint" << endl;
if(ec) //检查错误码
{
cout << ec.message() << endl;
return;
}

if(n != SIGINT) //检查信号是否要处理
{
return;
}

cout << "handler1 recv = " << n << endl;
cout << "do something" << endl;
//w.~work();
};

typedef void(handler_type)(const error_code&, int); //定义第二个handler,使用function
function<handler_type>
handler2 =
[&](const error_code& ec, int n) //函数签名必须符合要求
{
if(n != SIGUSR1)
{
return;
}

cout << "handler2 recv = " << n << endl;

sig.async_wait(handler1); //异步添加处理handler
sig.async_wait(handler2); //异步添加处理handler
};

sig.async_wait(handler1); //异步添加处理handler
sig.async_wait(handler2); //异步添加处理handler

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(); //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; //function对象,持有无参无返回的可调物
steady_timer m_t; //asio定时器对象
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);

//m_t.async_wait(bind( //bind绑定成员函数
// &this_type::handler, this, //传递this指针
// boost::asio::placeholders::error)); //error占位符传递错误码
// //_1));
}

void handler(const error_code&)
{
if (m_count++ >= m_count_max) //计时器到达上限退出
{ return; }

m_f(); //调用function对象

m_t.expires_from_now(200_ms); //设置定时器的终止时间为0.2秒之后

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;});

//timer_with_func t2(io, 5,
// []{cout << "hello timer2"<<endl;});

io.run(); //io_service等待异步调用结束
}

通信概述

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; //声明一个ip地址对象
addr = addr.from_string("127.0.0.1"); //字符串产生ip地址
assert(addr.is_v4()); //ipv4的地址
cout << addr.to_string() << endl; //转换成字符串输出
addr = addr.from_string("ab::12:34:56"); //ipv6的地址
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"); //ipv4地址
ip::tcp::endpoint ep(addr, 6688); //端点对象,端口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; //io_service对象

acceptor_type acceptor(io,
endpoint_type(ip::tcp::v4(), 6688)); //一套完成bind,listen
cout << acceptor.local_endpoint().address() << endl;

for(;;)
{
socket_type sock(io); //一个socket对象

acceptor.accept(sock); //accept阻塞等待socket连接

cout << "client:";
cout << sock.remote_endpoint().address() << endl;

sock.send(buffer("hello asio")); //send发送回去
}
}
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; //io_service对象

socket_type sock(io);
endpoint_type ep(address_type::from_string("127.0.0.1"), 6688);
//创建连接端点

sock.connect(ep); //socket进行连接端点
cout << sock.available() << endl; //获取可读取的字节数

//vector<char> str(sock.available() + 1, 0);
//sock.receive(buffer(str));
//cout << "recive from " << sock.remote_endpoint().address();
//cout << &str[0] << endl;

vector<char> str(5,0); //创建vector缓冲区
error_code ec;
for(;;)
{
sock.read_some(buffer(str), ec); //使用buffer包装缓冲区接收数据,并使用错误码形式
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));


// 可用bind的方式去绑定一个函数处理
// m_acceptor.async_accept(*sock,
// bind(&this_type::accept_handler, this,
// boost::asio::placeholders::error, sock));
//}

void accept() //用于启动异步接收连接
{
sock_ptr sock(new socket_type(m_io));


//需要调用acceptor的saync_accept函数启动异步接收连接
//使用了lambda方式去回调处理
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));

//异步发送数据,腰围这个异步调用编写回调函数write_handler
//发送完勿忘再次启动服务器接收连接,否则完成数据发送后io_service将因为没有事件处理而结束运行

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
//#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
#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; //io_service对象
buffer_type m_buf; //接收缓冲区
endpoint_type m_ep; //tcp端点
public:
client():
//m_buf(100,0),
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));
// sock->async_connect(m_ep,
// bind(&this_type::conn_handler,this,
// boost::asio::placeholders::error, sock));
//}

void start()
{
sock_ptr sock(new socket_type (m_io)); //创建socket对象

//function<void(const error_code&,std::size_t)> handler =
// [this, sock, handler](const error_code& ec, std::size_t)
// {
// if (ec)
// { return; }
// cout << &m_buf[0] << endl;

// //sock->async_read_some(buffer(m_buf), handler);
// };


//异步连接
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,
//boost::asio::placeholders::bytes_transferred,
sock));

//start();
}
void read_handler(const error_code& ec, /*std::size_t n,*/ 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,
//boost::asio::placeholders::bytes_transferred,
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对象

//创建一个query对象
ip::tcp::resolver::query q(name, boost::lexical_cast<string>(port));

auto iter = r.resolve(q); //使用resolve()迭代端点
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不仅能够解析域名,也支持使用Ip地址和服务名
resolve_connect(sock,"www.boost.org", 80);
cout << sock.remote_endpoint() << endl;

//ios.run();
}
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; //必需的io_Service对象

spawn(io, //使用spawn函数产生协程
[&](yield_context yield) //lambda表达式
{
acceptor_type acceptor(io, //acceptor对象
endpoint_type(ip::tcp::v4(), 6688));

for(;;)
{
socket_type sock(io);
error_code ec;

acceptor.async_accept(sock, yield[ec]); //使用协程 无handler

if(ec)
{
return;
}

auto len = sock.async_write_some( //异步写数据,获取字节数
buffer("hello coroutine"), yield); //使用协程,无handler
cout << "send " << len << " bytes" << endl;
} //监听服务结束
} //服务lambda结束
); //服务协程结束

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; //未初始化的optinal对象
optional<int> op1(none); //同上 none赋予未初始化值

assert(!op0); //bool测试
assert(op0 == op1); //比较两个optional对象
assert(op1.value_or(253) == 253); //获得缺省值

cout << op1.value_or_eval( //获得lambda定义的返回缺省值
[](){return 874;}) << endl;

optional<string> ops("test"); //初始化字符串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>
//using namespace boost::assign;

//////////////////////////////////////////
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);
// v = [1, 2, 3, 4, 5]

deque<string> d =
(list_of("power")("bomb"),"phazon","suit");
// d = [power bomb phazon suit]

set<int> s = (list_of(10), 20,30,40,50);
// s = {10 20 30 40 50}

map<int, string> m = list_of(make_pair(1, "one"))(make_pair(2, "two"));
// m = [(1, “one”) (2, “two”)]

map<int, int> m1 = map_list_of(1, 2)(3, 4)(5, 6);
//m1 = [(1, 2)(3, 4)(5, 6)]

map<int, string> m2 = map_list_of(1, "one")(2, "two");
//m2 = [(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);
//v = 1,2,2,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;
//ms = x,x,x,x,x,1,1,10
for(auto& x : ms)
cout << x << ",";
cout << endl;


deque<int> d;
push_front(d).range(v.begin(), v.begin() + 5);
//d = 3,2,2,2,1
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); //move语义,把a偷到tmp
a = std::move(b); //move语义,把b偷到a
b = std::move(tmp); //move语义,把tmp偷到b
}

但不是所有类都实现了自己的转移构造和赋值函数,所以自己写的类最好能够实现swap()来提供效率

解决方案有两种

  • 第一种直接利用函数重载,编写一个同名的swap函数,swap在调用内部的高效成员交换函数,这样就不用std::swap函数了

  • 第二种利用ADL查找模板特化的std::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 std
//{
//template<>
//void swap(point &x, point &y) //模板特化swap函数
//{ x.swap(y);}
//}

namespace boost {

void swap(point &x, point &y) //模板特化swap函数
{ 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> //继承singleton
{
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;
}