一、boost库的简单介绍
boost是一个准标准库,相当于STL的延续和扩充,它的设计理念和STL比较接近,都是利用泛型让复用达到最大化。不过对比STL,boost更加实用。 STL集中在算法部分,而boost包含了不少工具类,可以完成比较具体的工作。
boost主要包含一下几个大类:字符串及文本处理、容器、迭代子(Iterator)、算法、函数对象和高阶编程、泛型编程、模板元编程、预处理元编程、并发编程、数学相关、纠错和测试、数据结构、输入/输出、跨语言支持、内存相关、语法分析、杂项。 有一些库是跨类别包含的,就是既属于这个类别又属于那个类别。
在文本处理部分,conversion/lexcial_cast类用于“用C++”的方法实现数字类型和字符串之间的转换。 主要是替代C标准库中的 atoi、 itoa之类的函数。当然其中一个最大的好处就是支持泛型了。
format库提供了对流的“printf-like”功能。printf里使用%d、%s等等的参数做替换的方法在很多情况下还是非常方便的,STL的iostream则缺乏这样的功能。format为stream增加了这个功能,并且功能比原始的printf更强。
regex,这个不多说了,正则表达式库。如果需要做字符串分析的人就会理解正则表达式有多么有用了。
spirit,这个是做LL分析的框架,可以根据EBNF规则对文件进行分析。(不要告诉我不知道什么是EBNF)。做编译器的可能会用到。一般人不太用的到。
tokenizer库。我以前经常在CSDN上看到有人问怎么把一个字符串按逗号分割成字符串数组。也许有些人很羡慕VB的split函数。现在,boost的tokenizer也有相同的功能了,如果我没记错的话,这个tokenizer还支持正则表达式,是不是很爽?
array: 提供了常量大小的数组的一个包装,喜欢用数组但是苦恼数组定位、确定数组大小等功能的人这下开心了。
dynamic_bitset,动态分配大小的bitset,我们知道STL里有个bitset,为位运算提供了不少方便。可惜它的大小需要在编译期指定。现在好了,运行期动态分配大小的bitset来了。
graph。提供了图的容器和相关算法。我还没有在程序中用到过图,需要用的人可以看看。
multi_array提供了对多维数组的封装,应该还是比较有用的。
并发编程里只有一个库,thread,提供了一个可移植的线程库,不过在Windows平台上我感觉用处不大。因为它是基于Posix线程的,在Windows里对Posix的支持不是很好。
接下来的 数学和数值 类里,包含了很多数值处理方面的类库,数学类我也不太熟,不过这里有几个类还是很有用的,比如rational分数类,random随机数类,等等。
static_assert,提供了编译器的assert功能。
test库,一个单元测试框架,非常不错。
concept_check提供了泛型编程时,对泛型量的一点检查,不是很完善,不过比没有好。
数据类型类any,一个安全的可以包含不同对象的类。把它作为容器的元素类型,那么这个容器就可以包含不同类型的元素。比用void *要安全。
compressed_pair,跟STL里的pair差不多。不过对空元素做了优化。
tuple,呵呵,也许是某些人梦寐以求的东西。可以让函数返回多个值。
跨语言支持:python,呵呵,好东东啊,可以将C++的类和函数映射给python使用。以下为几个CSDN上的关于boost.python的中文资料:http://dev.csdn.net/article/19/19828.shtm,http://dev.csdn.net/article/19/19829.shtm,http://dev.csdn.net/article/19/19830.shtm,http://dev.csdn.net/article/19/19831.shtm
pool:内存池,呵呵,不用害怕频繁分配释放内存导致内存碎片,也不用自己辛辛苦苦自己实现了。
smart_ptr:智能指针,这下不用担心内存泄漏的问题了吧。不过,C++里的智能指针都还不是十全十美的,用的时候小心点了,不要做太技巧性的操作了。
date_time,这个是平台、类库无关的实现,如果程序需要跨平台,可以考虑用这个。
timer,提供了一个计时器,虽然不是Windows里那种基于消息的计时器,不过据说可以用来测量语句执行时间。
uitlity里提供了一个noncopyable类,可以实现“无法复制”的类。很多情况下,我们需要避免一个类被复制,比如代表文件句柄的类,文件句柄如果被两个实例共享,操作上会有很多问题,而且语义上也说不过去。一般的避免实例复制的方法是把拷贝构造和operator=私有化,现在只要继承一下这个类就可以了,清晰了很多。
value_initialized:数值初始化,可以保证声明的对象都被明确的初始化,不过这个真的实用吗?似乎写这个比直接写初始化还累。呵呵,仁者见仁了。
这里面除了regex、python和test需要编译出库才能用,其他的大部分都可以直接源代码应用,比较方便。其实这些库使用都不难。最主要的原因是有些库的使用需要有相关的背景知识,比如元编程、STL、泛型编程等等。
二、多线程:
1、thread库相关的,c++多线程是一个复杂的事情,windows MFC提供了CWinThread类,WaitForSingleObject等待回收线程;
Linux系统提供了createThread,thread join来回收线程。
boost::thread就比较方便了:
1)、boost::thread(boost::bind(&class::func,this)).detach()。thread()启动一个线程,bind线程绑定的需要执行的任务接一个函数指针,detach,父线程不需要管子线程的回收。
2)、boost::thread thd1(func());参数是一个函数调用thd1.join();父线程来进行回收。
2、线程同步用互斥量boost::mutex,锁用boost::mutex::scoped_lock,来进行时序控制。
比如boost::mutex::scoped_lock lock(m_mutex);互斥量相当于是钥匙,lock相当于是锁,当获取这个钥匙的锁生命周期结束后,则钥匙释放。
mutex可以看成是通配的钥匙。还有一种是AutoLock<ThreadMutexLock> lock (&_operMutex);
三、网络编程:
网络编程分为同步模式和异步模式,同步模式是有一个数据块客户端发送过来,服务端就必须处理完才能处理下一个数据块。
异步模式是客户端发送的数据块放入缓存队列,异步处理不阻塞,同步模式是阻塞式的。
1、同步模式:
服务端:
// 创建服务器对象
boost::asio::io_service ios;#asio编程必须的io_service对象,服务端和客户端创建socket和服务端创建acceptor对象要用
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::tcp::v4(),9800);#tcp协议的服务器所在的ip和网络编程开放的端口,客户端连接的ip和端口
boost::asio::ip::tcp::acceptor acceptor(ios,ep);
// 监听连接
while(1){
boost::asio::ip::tcp::socket sock(ios);#创建socket连接对象
acceptor.accept(sock);
cont<<sock.remote_endpoint().address()<<endl;
boost::thread(boost::bind(svr_handle,sock)).detach();
}
//交互处理
void svr_handle(boost::asio::ip::tcp::socket sock)
{
string msg;
sock.write_some(boost::asio::buffer("hello world"));
char msg[1024];
sock.read_some(boost::asio::buffer(msg));
cout<<"client send msg:"<<msg<<endl;
}
客户端:
// 创建客户端对象
boost::asio::io_service ios;#asio编程必须的io_service对象
boost::asio::ip::tcp::endpoint ep("127.0.0.1",9800);
//监听连接
std::shared_ptr<boost::asio::ip::tcp::socket> p_sock(ios);#创建socket连接对象
p_sock->connect(ep);
string msg;
p_sock->write_some(boost::asio::buffer(msg));
char buf[1024];
p_sock->read_some(boost::asio::buffer(buf));
2、异步模式
服务端:
监听由同步模式的accept()变成了async_accept();
读写由同步模式的write_some()、read_some()变成了async_write_some()、async_read_some()
异步模式关键是void Server::run(){ios.run();//io_service的run方法异步处理队列}。
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::tcp::v4(),9800);
shared_ptr<boost::asio::ip::tcp::socket> s_ptr(new boost::asio::ip::tcp::socket(ios));
boost::asio::ip::tcp::acceptor acc(ios,ep);
start_accept();
ios.run();
void start_accept()
{acc.async_accept(*s_ptr,boost::bind(handle_accept,s_ptr,1));}
void handle_accept(shared_ptr<boost::asio::ip::tcp::socket> s_ptr,boost::system::error_code ec)
{
if(err) return ;
socket_ptr sock(new ip::tcp::socket(service));
start_accept(sock);
}
当接收到客户端连接的时候,handle_accept被调用。当连接之后socket则就能使用了。
客户端:
连接由同步模式的connect()变成了async_connect();
读写由同步模式的write_some()、read_some()变成了async_write_some()、async_read_some()
异步模式关键是void Client::run(){ios.run();//io_service的run方法异步处理队列}。
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint ep("127.0.0.1",9800);
//监听连接
std::shared_ptr<boost::asio::ip::tcp::socket> p_sock(ios);#创建socket连接对象
p_sock->async_connect(ep,handle_connect);
ios.run();
void handle_connect(const boost::system::error_code ec)
{//如果ec返回成功了,就知道连接是成功的了}
当handle_connect连接上了,则ios.run()就会循环退出。
四、文件系统:
文件系统相关的方法一般都在boost::filesystem命名空间中。
boost::filesystem::path filepath(path);
filepath.parent_path();//获取父路径
filepath.filename();//包括文件扩展名
boost::filesystem::file_size(filepath);//获取文件大小,单位是字节
boost::filesystem::is_regular_file(path);//判断是否是普通文件
boost::filesystem::is_directory(path);//判断是否是目录
boost::filesystem::is_symlink(path);//判断是否是链接文件
五、两个处理二进制数值工具:vector<bool>和bitset.
C++标准为处理二进制数值提供了两个工具:vector<bool>和bitset.
vector<bool> 是对元素类型为bool的vector特化,它内部并不真正存储bool值而是以bit来压缩保存,使用代理技术来操作bit,造成的后果就是它很像容器,大多数情况下的行为与标准容器一样,但它不是容器,不满足容器的定义。
bitset与vector<bool> 类似,同样存储二进制,但它的的大小固定,而且比vector<bool>支持更多的位运算。
两者的优缺点:vector<bool>可以动态增长,但不能方便地进行位运算;bitset则可以方便地对容纳的二进制位做位运算,但是不能动态的增长。
boost.dynamic_bitset 的出现恰好填补了这两者之间的空白,它类似标准库的bitset,提供丰富的位运算,同时长度又是动态可变的。
类摘要
template <typename Block, typename Allocator>
class dynamic_bitset
{
public:
explicit dynamic_bitset();/// 构造函数
dynamic_bitset(const dynamic_bitset& b);
/// 基本操作
void swap(dynamic_bitset& b);
void resize(size_type num_bit, bool value = false);
void clear();
void push_back(bool bit);
void apend(Block block);
/// 操作符重载
bool operator[](size_type pos)const;
dynamic_bitset& operator&=(const dynamic_bitset& b);
dynamic_bitset& operator|=(const dynamic_bitset& b);
...
///
dynamic_bitset& set();
dynamic_bitset& reset();
dynamic_bitset& filp();
/// 测试
bool test(size_type n) const;
bool any() const;
bool none() const;
dynamic_bitset operator~()const;
size_type count() const;
/// 转型到 ulong
unsigned long to_ulong() cosnt;
/// 容量操作
size_type size()const;
size_type num_blocks() const;
size_type max_size() const;
bool empty() const;
/// 集合操作
bool is_subset_of(const dynamic_bitset& a)const;
bool is_proper_subset_of(const dynamic_bitset& a)const;
/// 查找元素
size_type find_first()const;
size_type find_next(size_type pos) const;
};
dynamic_bitset 几乎与std::bitset相同,包括接口和行为,唯一的区别是:dynamic_bitset的大小是在构造函数中又参数决定的,而且运行时时动态可变的。
注意:与vecor<bool>和bitset一样,dynamic_bitset不符合标准容器的定义,不是严格意义样的容器,不提供迭代器的支持。
创建与赋值
dynamic_bitset的第一个模板参数Block指示dynamic_bitset以什么整数类型存储二进制位,必须是一个无符号整数,默认是unsigned long。第二个参数Allocator是类内部使用的内存分配器,默认是std::allocator<Block>。
- 不带参数的构造函数创建一个大小为0的dynamic_bitset对象
- 传入参数制定dynamic_bitset的大小并赋初值,像标准容易一样
- 从另一个dynamic_bitset拷贝构造
- 从 01 字符串构造(与std::bitset一样都要求是std::string字符串)
dynamic_bitset<> db1; /// 空的dynamic_bitset 对象
dynamic_bitset<> db2(10); /// 创建一个大小为10的对象
dynamic_bitset<> db3(0x16,
BOOST_BINARY(10101)); //注意这里,使用了boost宏 BOOST_BINARY 构造一个编译期的二进制数
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[]
容器操作
dynamic_bitset 可以使用resize()成员函数在运行时调整容器的大小,扩展或者收缩都是允许的,并且可以用true/false或者1/0指定扩展后新元素的默认值。
如果是扩展,那么原来有的二进制位保持不变,新增的二进制位被指为指定的值:如果是收缩,那么收缩后容量的二进制位保持不变,多余的二进制位被抛弃,此时设置新元素的默认值是无意义的。
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;
assert(db.size() == 5);
db.clear(); /// 清空 效率比resize效率高
assert(db.empty()&& db.size()==0);
}
/// dynamic_bitset 使用Block来存储二进制位,因此,size()函数不能
/// 反映dynamic_bitset所占用的内存大小。
assert(dynamic_bitset<>(64).num_blocks()==1);
assert(dynamic_bitset<>(65).num_blocks()==2);
{
dynamic_bitset<> db(5,BOOST_BINARY(01001));
db.push_back(true); /// 实际上是项二进制最高位添加
assert(db.to_ulong() == BOOST_BINARY_UL(101001));
}
{
dynamic_bitset<> db(5,BOOST_BINARY(01001));
db.append(BOOST_BINARY(101));/// 实际上是项二进制最高位添加
assert(db.size() == sizeof(unsigned long)*8 + 5);
cout << db << endl; //0000000000000000000000000000010101001
}
位运算与比较运算
与vector<bool>相似,dynamic_bitset也运用了代理技术,重载了operator[], operator|等操作符,因此可以像普通数字一样访问其中的二进制位,而且能对二进制或者整个容器做任意位运算,当然吹不能做加减乘除
1
dynamic_bitset<> db1(4, BOOST_BINARY(1010));
db1[0] &= 1; ///按位与运算
db1[1] ^= 1; ///按位异或运算
cout << db1 << endl;/// 1000
dynamic_bitset<> db2(4, BOOST_BINARY(0101));
assert(db1 > db2);
cout << (db1 ^ db2) << endl;///1101
cout << (db1 | db2) << endl;///1101
访问元素
除了使用operator[]直接访问容器内的元素外,dynamic—bitset还有个成员函数用于测试或者重置二进制位。
1.test()函数检测第n位是否为1
2.如果容器中存在二进制位1,那么any()返回true
3.如果容器中不存在二进制位1,那么none()返回true
4.count()函数统计容器中所有值为1的元素数量
5.set()函数可以置全部或者特定的位置的值为0或者1
6.reset()可以置全部或者特定的位置的值为0
7.flip()可以反转全部或者特定的位置的值
8.find_first()从0位置开始查找,返回第一个值为1的位置
9.find_next(pos)从第pos位置开始差值,返回第一个值为1的位置,如果查找不到,返回npos.
dynamic_bitset<> db(4, BOOST_BINARY(0101));
assert(db.test(0) && !db.test(1));
assert(db.any() && !db.none());
assert(db.count() == 2); ///值为1 的个数
{
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);
}