C++ Boost--库之小工具

noncopyable

禁止拷贝构造函数生成有以下两种方法
1、私有化拷贝构造函数和拷贝赋值操作符(scoped_ptr采用了该方法)

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

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

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

当然这里面实际上也就是将赋值构造函数以及拷贝赋值声明直接 = delete,结果是一样的

ignore_unused(类似于Qt 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(断言,egt:BOOST_ASSERT(y != 0); // 如果y等于0,程序将在这里中止 )提供基本的安全保证 , 如果未初始化 , 那么函数的行为是未定义的。
"无意义"的值:
函数并不总能返回有效的返回值,很多时候函数可能返回"无意义"的值,这不意味着函数执行失败,而是表明函数正确执行了,但结果却不是有用的值。
表示返回值无意义最常用的做法是增加一个
"哨兵"的角色,它位于解空间之外,如NULL,-1,EOF,string::npos,vector::end()等。但这些做法不够通用,而且很多时候不存在解空间之外的"哨兵".
optional使用
"容器"语义,为这种"无效值"的情形提供了一个较好的解决方案。
optional很像一个仅能存放一个元素的容器,它
实现了"未初始化"的概念:如果元素未初始化,那么容器就是空的,否则,容器内就是有效的,已经初始化的值。

optional的真实接口很复杂,因为它要能够包装任何的类型。

操作函数
optional的模板类型参数T可以使任何类型,就如同一个标准容器对元素的要求,并不需要T具有缺省构造函数,但必须是可拷贝构造的。
可以有很多方式创建optional对象,例如:
无参的optional()或者optional(boost::none)构造一个未初始化optional对象,参数boost::none是一个类似空指针的none_t类型常量,表示未初始化。
optional(v)构造一个已初始化的optional对象,其值为v的拷贝。如果模板类型为T&,那么optional内部持有对引用的包装。
optional(condition, v)根据条件condition来构造optional对象,如果条件成立(true)则初始化为v,否则为未初始化。
此外optional还支持拷贝构造和赋值操作,可以从另一个optional对象构造。当想让一个optional对象重新恢复到未初始化状态时,可以向对象赋none值。
optional采用了指针语义来访问内部保存的元素,这使得optional未初始化时的行为就像一个空指针。它重载了operator*和operator->以实现与指针相同的操作,get()和get_ptr()可以以函数的操作形式获得元素的引用和指针。
成员函数get_value_or(default)是一个特别的访问函数,可以保证返回一个有效的值,如果optional已初始化,那么返回内部的元素,否则返回default。
optional也可以**用隐式类型转换进行bool测试(用于条件判断),就像一个队指针的判断。
optional还全面支持比较运算,包括
==,!=,<,<,>,>=。**与普通指针比较的"浅比较"(仅比较指针值)不同,optional的比较是"深比较",同时加入了对未初始化情况的判断。

#include <boost/optional.hpp>
#include <iostream>

int main()
{
	boost::optional<int> op0; //定义一个未初始化的optional对象
	boost::optional<int> op1(boost::none);//使用none赋值一个未初始化对象
	assert(!op0);//断点 false就会报错,BOOST_ASSERT也是一样的
	assert(op0 == op1);
	assert(op1.get_value_or(253) == 253); //获取可选值

	boost::optional<std::string> ops("test");//初始化字符
	std::string str = *ops;//解引用
	std::cout << str.c_str() << std::endl;

	std::vector<int> v(10);
	boost::optional<std::vector<int> &> opv(v);
	assert(opv);

	opv->push_back(5);//opv本来就是指针,当然可以push,只是optional当中就只有一个容器
	std::cout << opv->size() << std::endl;
	assert(opv->size() == 11);

	opv = boost::none;//重新变成未初始化的状态
	assert(!opv);

	system("pause");
	return 0;
}
#include <math.h>
#include <boost/optional.hpp>

boost::optional<double> calc(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));//条件构造函数
}
int main()
{
    boost::optional<double> d = calc(10);
    if (d)
        cout << *d <<endl;
    d = sqrt_op(-10);
    if (!d)
        cout << "no result"<<endl;

    system("pause");
    return 0;
}

工厂函数:
optional提供一个类似make_pair(),make_shared()的工厂函数make_optional(),可以根据参数类型自动推导optional的类型,用来辅助创建optional对象,声明:

optional<T> make_optional(T const &v);
optional<T> make_optional(bool condition, T const& v);

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

#include <boost/optional.hpp>
#include <boost/typeof/typeof.hpp>
int main()
{
    BOOST_AUTO(x, make_optional(5));//宏直接用 这个宏是在typeof头文件当中,是类型推导
    assert(*x == 5);
    BOOST_AUTO(y, make_optional<double>((*x > 10), 1.0));
    assert(!y);
    system("pause");
    return 0;
}

在这里插入图片描述

高级议题
异常处理
optional同STL容器一样,只提供基本的异常保证,不会超过被包装的类型T,它自身不抛出任何异常,只有在T构造时可能会抛出异常
就地创建
optional要求类型T具有拷贝语义,因为它内部会保存值的拷贝,但很多时候复杂对象的拷贝代价很高,而且这个值仅仅作为拷贝的临时用途,是一种浪费。
因此optional库提供出了"就地创建"的概念,可以不要求类型具有拷贝语义直接用构造函数所需的参数创建对象,这导致发展处了另一个Boost库in_place_factory。
代理示例:

#include <boost/optional.hpp>
#include <boost/utility/in_place_factory.hpp>
using namespace boost;
using namespace std;

int main()
{
    //就地创建string对象,不需要临时对象string("..")
    optional<string> ops(in_place("test in_place_factory"));
    cout<< (*ops).c_str()<<endl;
    
    //就地创建std::vector对象,不需要临时对象vector(10, 3)
    optional<vector<int>> opp(in_place(10, 3));
    assert(opp->size() == 10);
    assert((*opp)[0] == 3);

    system("pause");
    return 0;
}

引用类型
optional的模板参数类型可以使用引用(T&),它在很多方面与原始类型T有不同,比如无法使用就地创建就地赋值。与c++语言内置的引用类型不同的是,它可以声明时不指定初值,并且在赋值时转移包装的对象,而不是对原包装的对象赋值。

assign

容器赋值
在许多情况下,我们都需要为容器初始化或赋值,如初始错误代码和错误信息,或者是一些测试用的数据。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工厂函数
	for (int i = 0; i < _vector.size(); ++i)
	{
		std::cout << _vector[i] << " ";
	}
	std::cout << std::endl;
 
    //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工厂函数
}

int main()
{
	TestAssign();
	return 0;
}

在这里插入图片描述

容器初始化
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 引入的一种新的容器或数据结构
在这里插入图片描述
在这里插入图片描述
上面这些只能做容器初始化!重新赋值都是不行的 报类型不匹配的错

重复输入
在填充数据时会遇到输入重复数据的问题,如果用之前的方法要写大量的重复代码,很麻烦,也容易造成多写或少写的错误。 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
	for (int i = 0; i < _vector.size(); i++)
	{
		std::cout << _vector[i] << " ";
	}
	std::cout << std::endl;
    std::deque<int> _deque;//7 6 6 6 5
    boost::assign::push_front(_deque).range(_vector.begin(), _vector.begin() + 5);
	for (int i = 0; i < _deque.size(); i++)
	{
		std::cout << _deque[i] << " ";
	}
}

tribool
boost::tribool 类似 C ++ 内建的 bool 类型,但它基于三态布尔逻辑:在 true (真)和 false (假 ) 之外,还有一个 indeterminate 状态(未知、不确定)。三态布尔逻辑的一个例子场景是执行某项任务之前状态是不确定的(没有开始也没有结束),启动任务后状态是 true ,停止任务后状态是 false 。
tribool类很简单,它内部实现了三态 bool 值的表示,除了构造函数,没有其他成员函数。可以在创建 tribool 对象的同时传入三态 bool 值对它进行初始化,如果使用无参的默认构造函数,那么 tribool 的默认值是 false 。

tribool支持逻辑运算II && ! 及比较运算== !=
任何与 indeterminate的比较操作结果都是indeterminate。
在与 indeterminate 的逻辑或运算中,它只有与 true 的运算结果为 true,其他均为indeterminate。
在与 indeterminate 的逻辑与运算中,它只有与false的运算结果为false,其他均为indeterminate。
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;
    }
}

与optional当中的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

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
}

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

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

在这里插入图片描述

  • 16
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值