目录
一、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 && ! 及比较运算== !=
- 任何与 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;
}
}
与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)
}