• 学习Boost小结(一)


      记得有人说过这样一句话: "不了解boost就不知道C++的一半好". 

      ---------引

      C++是一个非常厉害的东西. Boost作为C++的准标准库, 还是有很大价值去深入围观的. 经过一段时间的学习, 现在进行一个小小的总结. 参考资料主要参照了罗剑锋前辈的<<Boost程序库完全开发指南>>和boost的官方文档. 

      

      0) boost的下载和安装

      在Boost的官网http://www.boost.org/上可以很容易的找到下载的链接. 截止到2012年6月28日, Boost的最新版本已经是1.50.0了. 下载压缩包, 解压即可. 这东西不分平台, win下还是linux下都是一样的压缩包. Boost里面大部分组件都可以直接使用(直接#include对应的头文件), 因此我也就没有用bjam去整个的编译. 在编译的时候把boost的路径加到编译选项中即可. 

      

      1) timer库

      这个是一个用来在程序中计算某一段代码运行时间的库. 实际上就是把C标准库中的clock()函数封装了一下, 使代码看起来更漂亮了. 

      

      2) date_time库

      能够方便的用来处理时间和日期. 历法什么的是一个很麻烦的东西. 比如日常使用的公历(也就是格里高利历), 一年有十二个月, 每个月天数都不一样, 还会遇上闰年啊什么的. 使用date_time库能够方便的计算一些和时间相关的问题, 比如计算两个日期之间间隔了多少天. 

     

      3) smart_ptr库

      内存管理始终是C++程序猿面对的一个大课题. 之前面试的时候也被反复问到过内存泄露相关的话题. 一些 "更高级" 的语言, 比如Java, C#, Python之流, 都用自动的所谓 "垃圾回收机制" , 也就是对于不使用的内存自动的释放掉, 可以避免内存泄露. 这样做一方面确实在一定程度上解放了程序猿, 但另一方面自动扫描 "不使用的内存" 的过程也损失了一定的运行效率. C++虽然没有内置垃圾回收装置, 但是也可以自助实现类似的东西, 一个比较精巧的实现可以参考<<C++编程艺术>>这本书(主要的实现思想就是用一个全局的表, 每次申请内存, 都把内存的地址在表中记录; 每隔一段时间就扫描这张表, 并释放里面不使用的内存). 对于C++来说, 还有一个方法可以有效在一定程度上解决内存泄露的问题, 就是使用智能指针. 所谓智能指针实际上是把一般的指针包装了下, 在智能指针对象的生命周期结束的时候就会自动的释放动态分配的内存. C++标准库里面以前有一个auto_ptr, 不过这个东西虽然很好的起到了自动释放内存的效果, 但并不完美. 对于智能指针来说一个比较关键的问题是处理智能指针的复制, 也就是说同样一块内存如何由多个智能指针进行协调管理. auto_ptr的策略是同一时间只有一个智能指针能够管理内存, 对auto_ptr进行复制的时候原指针会失效(实际上执行了类似于 "移动内存管理权" 的操作). 因此不能放在STL的容器中, 也不能当做函数的参数按值传递. 在Boost中使用了其他的管理策略. 其中两个最典型的, 分别是scoped_ptr和shared_ptr. scoped_ptr直接禁止了复制操作(将复制构造函数和赋值操作符设为私有), 试图对一个scoped_ptr进行复制就会在编译期发生错误. shared_ptr则是采用了一种非常经典的 "引用计数" 机制. 用一个计数器来记录有多少个shared_ptr在管理同一块内存, 当计数器的值为0时才真正的释放内存. 这样shared_ptr就可以复制了, 也就可以放入到STL容器中@气泡熊鼓掌@. 在C++11中这两种智能指针都被纳入了新标准(std::unique_ptr和std::shared_ptr). 值得一提的是shared_ptr是Boost中被非常高度评价的一个组件, 它光能起到自动管理内存的作用, 还可以有一些更加灵活的用法(比如利用shared_ptr生命周期结束时调用析构函数的时机, 来定制一些其他的操作(比如调用某个函数),  起到 "删除器" 的作用), 接口设计上还包含了一些设计模式的体现(使用make_shared()工厂函数可以消灭代码中的"new"), 另外它还是线程安全的. 用罗剑锋前辈的话说: "shared_ptr非常有价值, 非常重要, 非常有用", "在C++历史上曾经出现过无数的引用计数智能指针实现, 但没有一个比得上boost::shared_ptr, 在过去, 现在和将来, 它都是最好的". 与new相对应的内存申请方式还有new[], boost中提供了scoped_array和shared_array, 但是不如使用STL的容器更加方便安全. 

      

      4) pool库

       "pool" 这个概念应该也是计算姬领域一个非常经典也应用非常广泛的东西了. 由于申请内存操作是一个非常消耗时间的操作, 因此索性一次性的申请一大块内存, 然后每次需要内存的时候都从这块大内存上面进行分配, 在内存使用完毕后再统一回收. 避免了频繁申请内存, 可以大大提高程序的运行效率. 这个库中最主要的有两个类, pool和object_pool. 前者仅仅分配内存, 后者还会自动的调用构造函数和析构函数. 分别适用于管理内建类型和类类型的数据. 使用方法都很简单了, 调用malloc/free/construct/destroy等方法即可, 此处就不再赘述. 

      

      5) noncopyable

      这是一个很简单的组件, 表示了一种 "禁止复制" 的语义, 实际上只是把复制构造函数和赋值操作符都设置为私有了. 

      

      6) typeof

      C++的变量都是有一个明确的类型的. 有时候类型可能会很复杂, 写起来非常麻烦, 例如遍历一个容器: 

    for(std::map<std::string, std::string>::iterator it=the_map.begin(); it!=the_map.end(); ++it)

      定义一个迭代器要写很复杂一长串的东西, 很不方便, 也很容易弄错. Boost中提供了BOOST_AUTO宏, 可以在变量初始化时自动的识别出变量的类型. 

    for(BOOST_AUTO(it, the_map.begin()); it!=the_map.end(); ++it)

      这样代码看起来就不像刚才那么啰嗦了. 但还是看着有点别扭. 实际上在C++11中直接使用auto关键字, 可以达到更好的效果. 

    for(auto it=the_map.begin(); it!=the_map.end(); ++it)

      不过C++11中还有更漂亮的写法. 

    for(auto& x : the_map)

      同样是一行代码, 最后的一行比第一行简洁了实在太多了~在这里忍不住要感谢C++11带来了这种更"高级"更方便的写法. 

      

      7) optional

      提供了 "无效的值" 的概念. 比如一些函数的返回值, 如果是非法的结果, 经常会返回某个特定的值, 比如-1什么的. 但是如果-1也是一个合法的解呢? 例如一个求商的函数, 

    double divide(double x, double y)
    {
      if(y==0)
        return ERROR;
    return x/y; }

      如果y的值为0, 那么divide函数应该返回一个无效的值ERROR. 那么这个ERROR应该设定成什么值好呢? 很明显, 至少-1不行, , , , , 

      使用optional就可以解决这个问题. 

    optional<double> divide(double x, double y)
    {
        if(y==0)
            return optional<double>();
        return optional<double>(x/y);
    }

      嘛, 首先声明, double什么的直接用==和0比较是一个比较糟糕的事情, , , , , 正确方法应该是做差然后小于某个值, 这里这个例子就不弄的那么严谨了, , , , 

      用一个 "未初始化" 的optional对象来表示一个 "解空间之外的值" . optional的接口长的有点像智能指针, 也重载了operator*和operator->, 从而获取在optional对象中保存的真实的值. optional的构造函数还有一个重要的重载optional(bool condition, T v), 如果condition为true, 才构建了一个有效对象, 值为第二个实参的一个副本; 如果condition为false则表示构建了一个无效对象. 

      使用optional使逻辑更加严谨, 代码也看起来更统一了一些. 但是从个人角度而言, 并不是很喜欢这个东西~包装上一层之后感觉变的很臃肿, 明明是需要返回个double却返回optional<double>, 还得解引用了才能用~如果可能的话, 我还是会尽量选择返回一个一般一点的返回值作为错误码. 而且, C++还有异常机制, 也可以一定程度上解决这个 "无效值" 的问题. 

      

      8) assign

      老实说, 看到这个东西的时候, 我真的深深的震精了, , , , C++还可以这么玩?!感觉仿佛变成了另外一种语言. 通过重载 operator, 和 operator() , 给容器赋值变的一下就很直观和简洁了(我是指和使用一大堆push_back和insert相比). 但是仔细揣摩, 又觉得有的时候规则有些复杂了. 写一大串括号啊什么的未必就很漂亮, 而且有时候还需要一些辅助函数. 不过也没办法, 毕竟Boost仅仅是一个库而已, 在C++原有的语法框架上做到这种程度已经让我拍案叫绝了. 相比之下, 还是觉得C++11中的初始化列表更简单实用一些, 真正的把各种初始化的方法统一到了一起. 

      9) swap

      swap也算是计算姬领域一个使用相当广泛, 也相当频繁的东西了. STL中已经有了一个swap了. 但是std::swap使用的是一个非常通用的实现方法(使用一个临时空间, 然后调用赋值操作符). 对于一些复杂的对象, 可能存在更高效更方便的swap方法. 比如两个vector对象, 进行交换的时候只要交换管理内存的指针即可完成, 而使用std::swap的话, 就需要把vector中保存的元素也搬来搬去, 多做了好多无用功. Boost中的swap最主要的意义是这样的: 对于自定制的对象, 我们可以定制具体的高效的swap方法, 然后使用Boost中的swap可以自动的找到我们自定制的方法. 另外Boost的swap还可以高效的交换两个数组. 

      10) tribool

      三态bool, 除了true, false, 还有一个indeterminate. 嘛, 第三种状态的名字好长, 用户可以使用一个宏自定义第三态的名字, 比如改成unknown.

    BOOST_TRIBOOL_THIRD_STATE(unknown);

      因为是三态bool, 因此逻辑运算也变的更复杂了. 这些都是数学上的一些东西了, 具体可以参考一些数学方面的资料以及Boost官方文档. 

      值得一提的是tribool和optional<bool>之间的差别. tribool的第三态indeterminate表示 "不确定" , 而optional<bool>能够表示的是一个 "无效的值" . 二者是完全不同的意义. 

      11) operators

      C++高级编程这本书上反复强调过这么一句话: C++设计的两个原则是抽象和重用. 操作符重载, 模板, 面向对象等特性正是抽象和重用的重要的语言层面上的重要支撑. 对于一个自定制的类, 有时候需要重载一系列的操作符来完成一个完整的功能体系. 比如翻翻STL容器的源码, 其中不小的篇幅就是在重载各种操作符. 而其中很多操作符之间是具有相关性的: 比如<, >, <=, >=这四个操作符都是相互关联, +=和+和是相互关联. Boost的operators就是提供了这样一种机制, 对于具有相关性的运算符, 重载其中的一个, 其他的重载就可以自动完成. 具体使用的时候也很方便. Boost提供了一系列的包含不同的运算符的类, 使用的时候让自定制的类继承自这些基类即可. 

      12) exception

      提供了一个比std::exception更先进的异常类, 能够更灵活的使用, 也能够自定制更加人性化的错误提示信息. 嘛, 不过自身对于异常的理解也不够透彻, , 这部分内容也木怎么看懂, , , , , 不过直观感觉, 使用起来还是有点复杂的. 

      13) uuid

      提供了一个用来表示uuid的类, 并且提供了一些相关的用来生成一个uuid的算法. 老实说这个uuid有些不太领会究竟是怎么个东西, , 貌似在密码学中用处很大? 彩笔表示理解不能思密达. 

      14) lexical_cast

      一个用于类型转换的函数. 类型转换是一个非常常见的东西, 其中最常见的应该是数字和字符串之间的转换. 有多种方法可以完成数字和字符串之间的相互转换. 比如C标准库中的atoi(), itoa(), sprintf(), 都可以完成C风格字符串和数字之间的转换. C++的std::string的话, 旧标准中比较正统一些的做法是将一个string读入到一个stringstream中, 然后再写入到一个int中, 即可完成string到int的转换(反之亦然). lexical_cast内部的实现实际上就是使用sringstream. 经过这么一层包装, 代码就可以看起来更清晰了. lexical_cast进行转换的对象类型有一个要求, 就是源对象需要支持 operator<< , 目标对象需要支持 operator>> . 值得一提的是对于string和数字之间的转换C++11中有了新的方法, 使用一组to_string()函数将数字转换成std::string, 使用一组stox()的函数(x代表不同的转换的目标类型, 比如i代表int, l代表long)讲std::string转换成数字.

      15) format

      format在一定的应用场合下真心是个不错的东西. 提到format, 就不得不提到printf和cout. printf需要一个格式化的字符串, 用来设定具体的输出格式. 格式化字符串一方面能很方便的将格式排好, 另一方面各种格式符啊什么的, 记忆起来也好复杂好揪心, 像我这种难得用一次printf的人, , , , 除了%d表示十进制整数, %s表示C风格字符串, , , , , 剩下的就都布吉岛鸟, , , 不过不得不承认的是这种格式化字符串确实还是有着得天独厚的优势的. 在这方面上, format的格式化效果比printf做的更好. 使用的时候使用一个格式化字符串初始化一个format对象, 然后把需要格式化的数据用 operator% 输入到里面即可. 然后这个格式就可以重复使用好多次了.

      

      16) string_algo

      提供了一组处理字符串的算法. 针对的目标是std::string和std::wstring. STL中的算法都是 "泛型算法" . 泛型意味着通用, 也就是说在一些具体的问题中可能使用起来就不够完美. C++标准库中的string类也提供了一些字符串相关的算法, 查找替换啊什么的, 和其他语言相比还不够 "先进" . string_algo中可以方便的在忽略字符串大小写的前提上处理问题, 还提供了更强大的查找, 替换, 删除, 分隔, 合并, 修剪等算法. 

       

      17) tokenizer

      分词也是一个经常会遇到的问题. 比如一个string保存了一句话, 里面有若干个单词, 经常需要把里面的每个词都提取出来. C++中的官方做法, 可以使用stringstream来完成这项工作, 不过比较繁琐, 使用不便, 功能也比较有限. 相比之下tokenizer就强大了很多, 把一个字符串放到一个tokenizer对象中, 然后像容器一样使用迭代器遍历tokenizer对象中的每一个元素即可. 而且还可以很方便的自定制分隔符. 默认的分隔符是空格和各种标点. 不过tokenizer还有两个明显的缺点, 一个是只能支持使用单个字符作为分隔符进行分词, 另一个是对于wstring支持不佳. 

     

      18) xpressive

      Boost的一个正则表达式的库. 非常有特点的地方是这个库有两种方式使用正则表达式, 动态方式和静态方式. 对于这个库的核心类basic_regex, 动态和静态方式构造basic_regex对象的方式完全不同.  动态方式通过basic_regex::compile()这个工厂函数, 使用一个用来表示正则式规则的字符串来初始化basic_regex对象; 静态方式则展现了C++非常强大的战斗力, 通过运算符重载, 使用符合C++规则的语法构造了正则表达式的语法, 啊, 请原谅我匮乏的语言能力难以把这个东西表述清楚. 然后配合一些正则式的算法, 比如regex_match(), regex_search()等函数, 即可完成依据正则表达式对字符串的处理. 动态方式对正则式的解析发生在程序运行期, 静态方式对正则式的解析发生在编译期, 所以使用静态方式程序的运行效率要更高一些. 补充一点, xpressive可以处理两种字符串, C风格字符串和std::string, 处理c风格字符串系列的类的名字带有 "c" 前缀, string系列的类带有 "s" 前缀. 

      这里贴一小段静态方式的代码: 

      sregex reg=as_xpr('a')>>_d;
      string str("a1, a2, a3, b3, a6");
      smatch what;
      str=regex_replace(str, reg, "hello");
      cout<<str<<endl;

      通过重载了 operator>> , 把正则表达式的每一部分粘到一起. 

      动态方式和一般的正则表达式使用很类似. 静态方式执行效率比动态方式高, 但是需要花额外的时间理解构造正则表达式的方法. 不过也还好, 在Boost官方文档中有一张表格, 将静态方式的正则表达式和Perl的正则表达式进行了对比, 还是比较容易上手的. 

      纸上得来终觉浅, 学习某一种东西的最佳方式还是在实际的合适的环境中反复应用. 这段时间通过读书翻阅文档和写一些简单的demo对Boost中的部分组件进行了学习, 初步的感受到了Boost的强大. Boost是一个庞大的库, 限于水平, 本文也只是浅尝辄止的进行了归纳. 很多细节并未深入考究, 我以为首先在脑子中建立一个索引, 以后使用到的时候再具体深究是比较高效的方法. 之后的一段时间还是会继续的研究Boost, 相信这个东西还是会给我带来不少惊喜的~. 哦, 对鸟, , , , 公司还留了作业, 听说如果入职考的好还能加薪? @气泡熊鼓掌@看来还是很有必要花些时间去做做公司的作业的~~

     

      

  • 相关阅读:
    css3.0新属性效果在ie下的解决方案(兼容性)
    ajax实现md5加密
    ajax给全局变量赋值问题
    前端资源分享
    7件你不知道但可以用CSS做的事
    jQuery 常用效果
    ThinkPHP redirect 传参
    join和split 的使用
    JSON_FORCE_OBJECT 数字索引数组 强转对象
    layer 弹框不显示内容
  • 原文地址:https://www.cnblogs.com/HGtz2222/p/2591036.html
Copyright © 2020-2023  润新知