C++ I Boost 库之小工具

目录

一、noncopyable

二、ignore_unused

三、optional

四、assign

1.容器赋值

2.容器初始化

3.重复输入

五、tribool

 与option比较

六、uuid

1.UUID介绍

2.UUID生成器

Nil 生成器

字符串生成器

名字生成器

随机生成器

3.UUID转字符串

七、获取当前函数名 


一、noncopyable

        在 C++ 中定义一个类时,如果不明确定义拷贝构造函数和拷贝赋值操作符,编译器会为我们自动生成这两个函数。

class ToolsStudy{

};

当我们定义了一个ToolsStudy的空类,编译器在处理它时会“默默地”为它增加拷贝构造函数和拷贝赋值操作符,真实代码类似于:

 class ToolsStudy
{
public:
    ToolsStudy(const ToolsStudy &) {
        //...
    }
    ToolsStudy& operator=(const ToolsStudy&) {
        //...
    }
};

此时我们可以通过拷贝构造或者拷贝赋值操作符来创建对象:

    ToolsStudy tool;
    ToolsStudy tool1(tool);
    ToolsStudy tool2 = tool1;

但有时候我们不需要类的拷贝语义,希望禁止拷贝实例,这时我们有以下两种方法:

1.私有化拷贝构造函数和拷贝赋值操作符(scoped_ptr采用了该方法)

class ToolsStudy
{
public:
    ToolsStudy() {

    }
private:
    ToolsStudy(ToolsStudy const&);
    ToolsStudy& operator=(ToolsStudy const&);
};

2.将此类继承自boost::noncopyable

boost::noncopyable delete了拷贝构造及赋值操作符。(具体可看boost::noncopyable类的实现)

noncopyable( const noncopyable& ) = delete;
noncopyable& operator=( const noncopyable& ) = delete;

#include "boost/noncopyable.hpp"
class ToolsStudy:boost::noncopyable
{
};

二、ignore_unused

        编写代码的过程中有时会出现一些暂时用不到但又必须保留的变量,编译的时候会出现警告,利用boost里的ignore_unused组件可以解决这个问题。ignore_unused使用可变参数模板,支持任意数量、任意类型的变量,把它们作为函数的参数“使用”了一下,从而“骗”过编译器。

        ignore_unused库还可以作用于未使用的局部类型定义,ignore_unused库的模板用法与函数的用法类似,但它不需要函数参数,而是在模板参数列表里写出要忽略的类型。

#include "boost/core/ignore_unused.hpp"
void TestIgnoreUnUsed()
{
    int i;
    int j = 10;
    boost::ignore_unused(i);//否则会有警告:"i":未引用的局部变量
    std::cout << j << std::endl;

    typedef int myInt;
    boost::ignore_unused<myInt>();
}

三、optional

        在实际的软件开发过程中我们经常会遇到无效值的情况。例如,函数并不是总能返回有效值,很多时候即使函数正确执行,但其结果却不是合理的值。如果用数学语言来解释,这种情况就是返回值位于函数解空间之外。有些无效返回的情况可以用抛出异常的方式来通知用户,但在某些情况下,这样代价很高或不允许抛出异常,这时必须要以某种合理的、高效的方式通知用户。

        optional使用容器语义 , 包装了 " 可能产生无效值 "的对象,实现了未初始化的概念,为这种无效值的情况提供了更好的解决方案。optional 已经被收入C ++17 标准。

        optional库的核心类是 optional ,它很像是个仅能存放一个元素的容器,实现了未初始化的概念:如果元素未初始化,那么容器就是空的,否则,容器内的值就是有效的、已经初始化的值。

        optional采用了指针语义来访问内部保存的元素 ,这使得 optional 未初始化时的行为就像一个空指针 , 可以使用 operator bool ( ) 和 operator ! ( )来检测是否有效。

        optional也重载了 operator * 和 operator - > 以实现与指针相同的操作 , get ( ) 和 get _ ptr ( ) 能够以函数的形式获得元素的引用和指针 . 需要注意的是它们内部仅使用 BOOST_ASSERT提供基本的安全保证 , 如果未初始化 , 那么函数的行为是未定义的。

#include "boost/optional.hpp"
#include <vector>
void TestOptional()
{
    boost::optional<int> op0;
    boost::optional<int> op1(boost::none);
    if (!op0)
    {
        std::cout << "op0 is none" << std::endl;                        //op0 is none
    }
    if (op0 == op1)
    {
        std::cout << "op0 == op1" << std::endl;                         //op0 == op1
    }
    std::cout << op1.value_or(10) << std::endl;                         //10
    std::cout << op1.value_or_eval([]() {return 11; }) << std::endl;    //11

    boost::optional<std::string> op2("hello world");
    std::cout << *op2 << std::endl;                                     //hello world

    op2.emplace("boost study",5);
    std::cout << *op2 << std::endl;                                     //boost
   
    std::vector<int> _vector(10);
    boost::optional<std::vector<int>&> op3(_vector);
    op3->push_back(2);
    std::cout << op3->size()<<","<<(*op3)[10] << std::endl;             //11,2
    op3 = boost::none;
    if (!op3)
    {
        std::cout << "op3 is none" << std::endl;                        //op3 is none
    }
}

         optional提供一个类似make _ shared 的工厂函数make_optional ( ) ,可以根据参数类型自动推导optional的类型,用来辅助创建 optional 对象。

        make_optional ( ) 无法推导出 T 引用类型的optional对象,如果需要一个 optional < T & > 的对象就不能使用 make_optional ( ) 函数。

        make_optional ( ) 也不支持 emplace 的用法 , 可能存在值的拷贝代价。

boost::optional<double> aclc(int x)
{
    return boost::optional<double>(x != 0, 1.0 / x);
}

boost::optional<double> sqrt_op(double x)
{
    return boost::optional<double>(x >= 0, sqrt(x));
}

void TestOptional2()
{
    boost::optional<double> d = aclc(10);
    if (d) {
        std::cout << *d << std::endl;
    }
    d = sqrt_op(-4);
    if (!d)
    {
        std::cout << "no reason" << std::endl;
    }
    auto x = boost::make_optional(5);x类型:boost::optional<int>

    auto y = boost::make_optional(*x > 2, 1.0);//y类型:boost::optional<double>
}

四、assign

1.容器赋值

        在许多情况下,我们都需要为容器初始化或赋值,如初始错误代码和错误信息,或者是一些测试用的数据。boost.assign可以代替重复调用 insert ( ) 或 push_back ( ) 等成员函数来初始化C ++98 中标准容器的方法。

        assign库重载了赋值操作符 " += " 、逗号操作符 " , "和括号操作符 " ( ) " , 这样就可以用非常简洁的语法非常方便地对标准容器赋值或初始化,这在需要填入大量初值的地方很有用。

        assign库提供 3 个工厂函数 : push_back ( ) 、push_front ( ) 和 insert ( )。这些函数可作用于拥有同名成员函数的容器,接收容器实例作为参数,创建对应的 Iist_inserter 对象。 assign库重载赋值操作符 " += "的实现原理就是调用 push_back ( ) 或 insert ( )函数。

        operator += 作用于容器时会调用工厂函数push_back( ),产生一个 list_inserter 对象,以这个对象为起点,随后的 【operator ( )】和 【operator,】就会依次被执行,使用 push_back( )或 insert ( )向容器插入数据。由于 Iist_inserter 重载了很少使用的逗号操作符,所以函数的调用得到了极大的简化。

        使用 assign 库时必须使用 using 指示符,只有这样才能让重载的“ += ”和“,”等操作符在作用域内生效。

#include "boost/assign.hpp"
using namespace boost::assign;

void TestAssign()
{
    //1 2 3 4 5 6
    std::vector<int> _vector;
    _vector += 1, 2, 3;
    _vector.push_back(4);
    boost::assign::push_back(_vector)(5)(6);        //使用push_back工厂函数

    //said : hello world !
    std::list<std::string> _list;
    _list += "hello", "world";
    _list.push_front(":");
    boost::assign::push_front(_list)("said");       //使用push_front工厂函数
    boost::assign::push_back(_list)("!");           //使用push_back工厂函数

    //C# C++ hello java python world
    std::set<std::string> _set;
    _set += "hello", "world";
    _set.insert("C++");
    boost::assign::insert(_set)("C#")("java"),"python";      //使用insert工厂函数

    //(1,hello) (2,world) (3,C++) (4,C#) (5,java)
    std::map<int, std::string> _map;
    _map += std::make_pair(1, "hello"), std::make_pair(2, "world");
    _map.insert(std::make_pair(3, "C++"));
    boost::assign::insert(_map)(4, "C#")(5, "java");//使用insert工厂函数
}

2.容器初始化

        list_inserter 解决了对容器的赋值问题,但有的时候我们需要在构造容器的时候就完成数据填充,这就用到了generic_list,这种方式较赋值更为高效。

        assign库提供 3 个工厂函数 list_of ( ) 、map_list_of ( )/pair_list_of ( ) 和tuple_list_of ( ) , 它们能够产生 generic _ list 对象,我们就可以像使用 list_inserter —样使用operator ( ) 和 operator,来填充数据。因为 generic_list 提供容器类型的隐式转型操作,所以它可以赋值给任意容器,当然我们也可以显式地调用容器转换函数。

        list_of ( ) 函数可以全部使用括号操作符,也可以把括号与逗号结合起来使用,但使用后者时需要将整个 list_of 表达式用括号括起来,否则编译器会无法推导出 list_of 表达式的类型导致赋值失败。

        map_list_of( ) / pair_list_of( )主要用来处理map容器【因为list_of处理map容器不方便】。

        tuple_list_of 用于初始化元素类型为 tuple 的容器,tuple是 Boost 引入的一种新的容器或数据结构。

void TestGenericList()
{
    //boost::assign::list_of
    std::vector<std::string> _vector = (boost::assign::list_of("hello"), "world");
    std::set<int> _set = boost::assign::list_of(1)(2)(3);
    std::map<int, std::string> _map = boost::assign::list_of(std::make_pair(1, "hello"))( std::make_pair(2,"world"))(3,"C++");
    //boost::assign::map_list_of
    _map = boost::assign::map_list_of(1, "hello")(2, "world");
}

3.重复输入

        在填充数据时会遇到输入重复数据的问题,如果用之前的方法要写大量的重复代码,很麻烦,也容易造成多写或少写的错误。 list_inserter 和 generic_list提供了成员函数 repeat ( )、 repeat_fun( )和range ( ) 来减轻工作量。

void TestRepeat()
{
    std::vector<int> _vector = boost::assign::list_of(5).repeat(3, 6)(7)(8);//5 6 6 6 7 8

    std::deque<int> _deque;//7 6 6 6 5
    boost::assign::push_front(_deque).range(_vector.begin(), _vector.begin() + 5);
}

五、tribool

        boost::tribool 类似 C ++ 内建的 bool 类型,但它基于三态布尔逻辑:在 true (真)和 false (假 ) 之外,还有一个 indeterminate 状态(未知、不确定)。三态布尔逻辑的一个例子场景是执行某项任务之前状态是不确定的(没有开始也没有结束),启动任务后状态是 true ,停止任务后状态是 false 。

        tribool类很简单,它内部实现了三态 bool 值的表示,除了构造函数,没有其他成员函数。可以在创建 tribool 对象的同时传入三态 bool 值对它进行初始化,如果使用无参的默认构造函数,那么 tribool 的默认值是 false 。

        tribool支持逻辑运算II  &&  ! 及比较运算==  !=

  1. 任何与 indeterminate的比较操作结果都是indeterminate。 
  2. 在与 indeterminate 的逻辑或运算中,它只有与 true 的运算结果为 true,其他均为indeterminate。
  3. 在与 indeterminate 的逻辑与运算中,它只有与false的运算结果为false,其他均为indeterminate。
  4. indeterminate 的逻辑非操作的结果仍为indeterminate。

        自由函数indeterminate  ( )  可以判断一个 tribool是否处于不确定状态。在处理 tribool 的不确定状态时必须要小心,因为它既不是 true ,也不是 false ,使用它进行条件判断永远都不会成立,判断不确定状态时必须要使用indeterminate ( ) 函数。

        作为第三态类型的名字, indeterminate 很清晰明确但可能有些长,所以 tribool 允许把 indeterminate 改变成任意用户喜欢的名字,常用的名字是 unknown、 maybe 等。只需要在全局域内使用宏 _BOOST _ TRIBOOL _ THIRD _ STATE 就可以为第三态更名,然后我们就可以随意使用这个新名字来代替原来的indeterminate。

#include "boost/logic/tribool.hpp"
BOOST_TRIBOOL_THIRD_STATE(unknown)
void TestTribool()
{
    //boost::tribool tb1(boost::indeterminate);
    boost::tribool tb1(unknown);
    boost::tribool tb2(true);
    boost::tribool tb3(!tb2);
    boost::tribool tb4;
    if (boost::indeterminate(tb1))//或者:if (unknown(tb1))
    {
        std::cout << "不确定" << std::endl;
    }
    if (!tb3 && !tb4)
    {
        std::cout << "tb3与tb4都为false" << std::endl;
    }
}

 与option<bool>比较

        optional < bool > 的语义是未初始化的 bool ,它是无意义的值,而 tribool 的 indeterminate 是已经初始化的有意义值,它表示 bool 值不确定。这两者存在着细微但十分重要的差别。

        选择 optional < bool > 还是 tribool 需要由具体的业务逻辑来决定。如果返回值是无效的(不存在有效的返回值),那么就选择 optional < bool >;如果返回值总是确定的,但可能无法确定其意义,那么就用 tribool。

    boost::optional<bool> op1;
    if (!op1)//并不表示op1为false
    {
        std::cout << "op1未初始化" << std::endl;
    }
    boost::optional<bool> op2(false);
    if (op2&&!(*op2))//表示op2为false
    {
        std::cout << "op2已初始化并且值为false" << std::endl;
    }

六、uuid

1.UUID介绍

        UUID是 Unversally Unique Identiner 的缩写,其别名是 GUID,它是一个 128 位的数字 (由32个16进制数组成。一个十六进制数占4位,也就是半个字节,那么UUID就是16个字节 ),它无须中央认证机构即可创建全球唯一的标识符,如 E4AOD7CE-9E6D-4E74-9E6D-7E749E6D7E74 就是一个 UUID。

        uuid库是一个小的实用工具,它可以表示并生成UUID。uuid位于名字空间 boost::uuids,但它没有一个集中的头文件,而是把功能分散在若干小文件中,因此需要包含数个头文件。

        uuid内部使用一个 16 字节的数组 data 作为 UUID值的存储,这个数组是 public 的,因此可以任意访问此数组,如拷贝或赋值。因此,某种程度上,可以把 uuid 看作一个容量固定为 16 、元素类型为unsigned char 的容器。

        基于 data 数组,uuid提供了 begin ( ) 和 end ( ) 的迭代器支持,可以像操作一个容器一样操作 UUID 值的每个字节。

        成员函数 size ( ) 和静态成员函数static_size ( ) 可以获得 UUID 值的长度,它是一个固定值,总返回 16。

        在数量庞大的 UUID 中有一个特殊的全零值 nil,它表示一个无效的 UUID,成员函数 is_nil( )可以检测 uuid 是否是 nil 。

        可以使用不同的算法来生成UUID ,这些算法使用枚举类型 version_type 来标识, version()函数可以获得 UUID 的算法版本。 uuid 类可以识别现有的五种生成算法:

  • 基于时间和MAC的算法 ( version_time_based )  
  • 分布计算环境算法 ( dce_security )
  • MD5摘要算法 ( version_name_based_md5 ) 
  • 随机数算法  (version_random_number_based)  
  • SHA1摘要算法 ( version_name_based_sha1) 
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
void TestUUID()
{
    boost::uuids::uuid u;
    std::cout << u.size() << "," << boost::uuids::uuid::static_size() << std::endl;//16,16
    std::cout << u << std::endl;      //cccccccc-cccc-cccc-cccc-cccccccccccc

    std::fill_n(u.begin(), u.size(), 228);//全部填为E4,228的十六进制
    std::cout << u << std::endl;      //e4e4e4e4-e4e4-e4e4-e4e4-e4e4e4e4e4e4

    std::fill_n(u.begin(), u.size(), 0);//全部填为0
    std::cout << u << std::endl;      //00000000-0000-0000-0000-000000000000
    if (u.is_nil())
    {
        std::cout << "u is nil" << std::endl;  //u is nil
    }
    std::cout << u.version() << std::endl;     //-1: version_unknown
}

2.UUID生成器

        用上述方法创建的UUID值很容易重复,如果要创建属于自己的 UUID 值,我们需要使用UUID 生成器。uuid库提供了四种生成器: Nil 生成器、字符串生成器、名字生成器和随机生成器。它们都是函数对象,重载了 operator( ),可以直接调用它们生成 uuid 对象。

Nil 生成器

        Nil生成器是最简单的 UUID 生成器,只能生成一个无效的 UUID 值(全零),它的存在只是为了方便地表示无效的 UUID 值。Nil生成器的类名是 nil_generator ,另外有一个内联函数 nil_uuid( ),相当于直接调用 Nil 生成器。

    boost::uuids::uuid nil_u = boost::uuids::nil_generator()();
    std::cout << nil_u << std::endl;
    nil_u = boost::uuids::nil_uuid();//00000000-0000-0000-0000-000000000000
    std::cout << nil_u << std::endl; //00000000-0000-0000-0000-000000000000

字符串生成器

        字符串生成器 string_generator 可以从一个字符串创建出 uuid 对象,字符串可以是 C 字符串、string、 wstring,也可以是一对迭代器指定的字符序列的区间。

        string_generator 对字符串的格式有严格的要求,有两种格式是它可以接收的 : ―种是没有连字符的全十六进制数字;另一种是使用连字符,但必须符合 UUID 的定义,在第 5 、 7 、 9 、 11 字节前使用连字符 , 在其他位置不允许出现连字符。也可以使用花括号将 UUID 字符串括起来,除了花括号,不能出现十六进制数以外的任何字符,否则会抛出 runtime _ error 异常。

    boost::uuids::string_generator sgen;
    boost::uuids::uuid string_u = sgen("12345678910111213141516171819202");//没有连字符的全十六进制数字
    std::cout << string_u << std::endl;//12345678-9101-1121-3141-516171819202
    string_u = sgen("12345678-9101-1121-3141-516171819202");//有连字符的全十六进制数字
    std::cout << string_u << std::endl;//12345678-9101-1121-3141-516171819202
    string_u = sgen("{12345678910111213141516171819202}");//连字符可有可无
    std::cout << string_u << std::endl;//12345678-9101-1121-3141-516171819202

名字生成器

        名字生成器name_generator使用基于名字的SHA1摘要算法,它需要先指定一个基准 UUID,然后使用字符串名字派生出基于这个 UUID 的一系列 UUID。名字生成器的典型的应用场景是为一个组织内的所有成员创建 UUID 标识,只要基准 UUID 不变,那么相同的名字总会产生相同的UUID。

    boost::uuids::uuid _string_u = boost::uuids::string_generator()("12345678-9101-1121-3141-516171819202");
    boost::uuids::name_generator ngen(_string_u);
    boost::uuids::uuid name_u = ngen("fcj");
    std::cout << name_u << ":" << name_u.version() << std::endl;//74787cf4-ba04-5311-9df3-90830628903d:version_name_based_sha1
    name_u = ngen("junjun");
    std::cout << name_u << std::endl; //16c2a822-072d-5a1d-8a30-bf493f2764f6

随机生成器

        随机生成器采用随机数生成 UUID,uuid 库使用Boost库的另一个组件 random 作为随机数的发生源,它可以产生高质量的伪随机数,保证生成的随机 UUID 不会重复。

    boost::uuids::random_generator rgen;
    boost::uuids::uuid random_u = rgen();
    std::cout << random_u << ":" << random_u.version() << std::endl;//770e7081-65b3-4dc4-9612-fa63df0c0857:version_random_number_based

3.UUID转字符串

        uuid可以使用字符串生成器从字符串生成,相应地也提供了转换成字符的操作,需要使用两个自由函数:to_string()或to_wstring()。

    std::string str = boost::uuids::to_string(random_u);
    std::cout << str << std::endl;//770e7081-65b3-4dc4-9612-fa63df0c0857

七、获取当前函数名 

#include <boost/current_function.hpp>
void PrintFunctionName()
{
    std::string name = BOOST_CURRENT_FUNCTION;
    std::cout << name << std::endl; //void __cdecl PrintFunctionName(void)
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烫青菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值