Boost组件实用工具
来源:互联网 发布:淘宝激光脱毛仪有用吗 编辑:程序博客网 时间:2024/06/05 09:03
《C++11/14高级编程:Boost程序库探秘》笔记
讨论一些功能比较简单,但实现原理却涉及C++语言深层次概念细节的Boost组件。
compressed_pair
compressed_pair库提供一个和std::pair非常相似的模板类compressed_pair,但它针对空类成员进行了特别的优化,可以”压缩“pair的大小。
空类:指的是没有非静态成员变量(静态成员不会增加类实例大小),也没有虚函数(会导致虚表指针)的class/struct类型。
空类不含有任何数据成员,理论上不应该占据内存,但是实际使用时仍然需要占用一定的内存空间,因为C++不允许存在0大小的对象,所以编译器会暗地里在类中插入一个char使它至少有1字节的大小。
compressed__pair利用了编译器的空基类优化技术,通过模板偏特化知道模板参数是否为空类,如果是空类,它就从空类protected继承,而不是作为成员变量。
compressed__pair的类摘要:
template<class T1,class T2>class compressed__pair{public: typedef T1 first_type; typedef T2 second_type; ... compressed__pair(); compressed__pair(first_param_type x,sencond_param_type y); explicit compressed__pair(first_param_type x); explicit compressed__pair(second_param_type y); first_reference first(); first_const_reference first() const; second_reference second(); second_const_reference second() const; void swap(compressed__pair& y);};
compressed_pair不提供任何比较操作符重载。访问成员需要使用函数形式的first()和second(),它们返回内部成员的引用,可以当作左值被赋值,也可以当作右值。
compressed_pair库在boost::details子名字空间下定义了一个模板类compressed_pair_imp,是compressed_pair的实现类,被用于私有继承:
template<class T1,class T2,int Version>class compressed_pair_imp;
它根据模板参数类型是否相同和成员是否为空这三个条件共定义了6个偏特化的compressed_pair_imp实现,再用type_traits库的is_same、is_empty、remove_cv以及details空间里的compressed_pair_switch进行编译期元计算,决定int模板参数Version的值,从而确定使用哪个版本的compressed_pair_imp。
checked_delete
checked_delete是对C++关键字delete的增强版,可以在编译期保证delete或delete[]删除的是一个指向”完整类型“的指针,避免在运行时发生未定义行为。
所谓不完整类型指仅有声明而没有定义的类,通常见于类的前向声明。
checked_delete库包含两个函数和两个函数对象,分别为:
两个函数
checked_delete:用于删除普通指针
checked_array_delete:用于删除数组指针,相当于delete[]两个函数对象(模板类)
checked_deleter
checked_array_deleter
模板函数的用法和delete、delete[]基本等价,只是把操作指针变量的delete表达式改成函数调用式:
auto p1 = new int(10);checked_delete(p1);auto p2 = new int[10];checked_array_delete(p2);//也可以删除对象指针,demo_class为一个自定义的类auto p3 = new demo_class;checked_delete(p3);auto p4 = new demo_class[10];checked_array_delete(p4);
模板类重载了operator(),可以像函数一样被调用,但不具备自动推导模板参数的功能,使用时必须用模板参数指明要删除的对象类型。
template<class T>struct checked_deleter{ typedef void result_type; typedef T* argument_type; void operator()(T* x) const;};template<class T>struct checked_array_deleter{ typedef void result_type; typedef T* argument_type; void operator()(T* x) const;};//使用时,圆括号调用构造函数生成一个临时函数对象,然后使用operator()调用删除功能auto p1 = new demo_class;checked_deleter<demo_class>()(p1);auto p2 = new demo_class[10];checked_array_deleter<demo_class>()(p2);
上面的函数对象的用法似乎很麻烦,因为它和checked_delete()是完全一样的,确增加了书写,但因为它的定义完全符合C++标准规范,故可以传递给那些需要函数对象的泛型代码,比如搭配标准库算法操作容器里的指针:
vector<demo_class*> v;v.push_back(new demo_class);v.push_back(new demo_class);for_each(v.begin(),v.end(),checked_deleter<demo_class>());
checked_delete的实现原理相当简单,全部实现代码如下:
template<class T> inline void checked_deleter(T* x){ typedef char type_must_be_complete[sizeof(T)?1:-1]; (void) sizeof(type_must_be_complete); delete x;}
它通过typedef定义了一个数组类型,大小由要被删除的类型T确定,如果T是一个完整的类型,那么sizeof(T)是一个正整数,数组大小为1,否则,sizeof(T)是0,数组大小为-1,但C++中数组定义不允许为负数,所以会引发一个编译错误。checked_array_delete实现类似。
addressof
addressof是对C++取址操作(&)的增强,已被收入C++标准(头文件<memory>),主要是防止程序员重载操作符&后,使用&取址出错的问题。
addressof是一个模板函数,与checked_delete类似,用法简单。
它的实现原理是使用了复杂的转型操作,核心实现如下:
reinterpret_cast<T*>(&const_cast<char&>( reinterpret_cast<const volatile char &>(v)));
T是addressof的模板类型参数,v是T的一个引用,addressof先使用reinterpret_cast转型操作把v强制解释成char类型,然后再将其重新解释成T*类型,这样就得到了变量的真正地址。
因为使用了多次运行时转型,所以addressof的运行效率没有原始的operator&高。
base_from_member
用于解决一种情况:当基类需要由派生类的成员变量来初始化时,C++的类初始化顺序要求基类必须在派生类之前完成初始化,一般的解决方法是把派生类的成员移到另一个辅助基类中,base_from_member使用多重继承和模板技术提供了这个用成员初始化基类的惯用法。
它的类摘要如下:
template<typename MemberType,int UniqueID = 0>class base_from_member{protected: MemberType member; //成员变量 base_from_member(); base_from_member(T1 x1); base_from_member(T1 x1,T2 x2); ... //默认情况下,有11个构造函数,最大支持10个参数,参数被用来在构造时初始化member成员变量。这个数量可以通过在包含头文件前定义宏BOOST_BASE_FROM_MEMBER_MAX_ARITY修改};
用法:
class derived : private base_from_member<complex<int>>, //第一个成员 private base_from_member<string,1>, //使用第二个模板参数 private base_from_member<string,2>, //用于区分不同的类型 public base,public base2{ //complex<int> c; //不直接声明成员变量 //typedef简化base_from_member代码的编写 typedef base_from_member<complex<int>> pbase_type; typedef base_from_member<string,1> pbase_type1; typedef base_from_member<string,2> pbase_type2;public: derived(int a, int b) : pbase_type(a, b), pbase_type1("str1"), pbase_type2("str2"), base(pbase_type::member), //用基类名字限定来使用成员变量 base2(pbase_type1::member,pbase_type2::member) { cout << member << endl; //成员变量的名字是member }};
conversion
标准转型操作符
C++标准为显式类型转换定义了四个新的转型操作符:
- const_cast:用于增加或者去除const、volatile修饰。
- static_cast:可以显式执行所有编译器可执行的隐式类型转换操作,不能执行多态类的交叉转型。
- dynamic_cast:用于多态对象(即存在虚函数的对象)间的类型转换,可以向上或者向下转换对象的类型
- reinterpret_cast:对目标的内存二进制位进行低层次的重新解释
const_cast
去掉const属性:const_cast<int*> (&num),常用,因为不能把一个const变量直接赋给一个非const变量,必须要转换。
加上const属性:const int* k = const_cast<const int*>(j),一般很少用,因为可以把一个非const变量直接赋给一个const变量,比如:const int* k = j;
class A {public: int m_iNum{ 0 };};int main(){ //常量指针被转化成非常量指针,转换后指针指向原来的变量(即转换后的指针地址不变)。 //1. 指针指向类 const A *pca1 = new A; A *pa2 = const_cast<A*>(pca1); //常量对象转换为非常量对象 pa2->m_iNum = 200; //fine //转换后指针指向原来的对象 assert(200 == pca1->m_iNum); assert(200 == pa2->m_iNum); //2. 指针指向基本类型 const int ica = 100; int * ia = const_cast<int *>(&ica); *ia = 200; assert(100 == ica); assert(200 == *ia); //常量引用转为非常量引用 A a0; const A &a1 = a0; A a2 = const_cast<A&>(a1); a2.m_iNum = 200; assert(0 == a0.m_iNum); assert(0 == a1.m_iNum); //常量对象(或基本类型)不可以被转换成非常量对象(或基本类型)。 const A ca; A a = const_cast<A>(ca); //error const int i = 100; int j = const_cast<int>(i); //error return 0;}
reinterpret_cast
很接近老式的显式类型转换,可以变更被转换对象的含义,最常用的场景是把一个已经失去类型信息的指针(如void*)重新解释,使其获得正确的类型。
static_cast
- 用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换(把子类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的
- 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证
- 把void指针转换成目标类型的指针(不安全!!)
- 把任何类型的表达式转换成void类型
注意:static_cast不能转换掉const、volitale、或者__unaligned属性
dynamic_cast
主要用于执行“安全的向下转型(safe downcasting)”和交叉转型。将基类的指针或引用安全的转换为派生类的指针或引用。
struct B1{ virtual ~B1(){}};struct B2{ virtual ~B2(){}};struct D1 : B1, B2{};int main(){ D1 d; B1* pb1 = &d; B2* pb2 = dynamic_cast<B2*>(pb1); //交叉转型,只要B1存在多态,dynamic_cast允许这种转型 B2* pb22 = static_cast<B2*>(pb1); //不允许,编译失败 return 0;}
但是dynamic_cast对两种转型失败后的行为不一致,会引起程序员的困扰。
对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针;
对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用。
conversion库针对dynamic_cast提供了增强的转型操作,用两个更安全命名更清晰的多态转型操作polymorphic_downcast和polymorphic_cast区分了多态对象的向下转型和交叉转型。
polymorphic_downcast
polymorphic_downcast提供对多态对象指针的向下转型功能,它使用static_cast提供高效的转型操作,但不具备dynamic_cast的错误检测功能。
template<class Target,class Source>inline Target polymorphic_downcast(Source* x){ BOOST_ASSERT(dynamic_cast<Target>(x) == x);//断言转型成功 return static_cast<Target>(x);}
我不能理解static_cast原本是用来做向上转型的,这里介绍它用来做向下转型的什么意思。感觉这个工具不太好理解,不太好用,还不如C++标准的dynamic_cast,看了一下后面,还不能用来转型引用,只能操作指针,所以,算了。
numeric conversion
C++标准为数值的极限提供了模板类numeric_limits,它以数值类型(char、int、double等)为参数模板,用静态成员变量和静态成员函数给出了数值的一些相关特征,但在处理整数和浮点数的极值时并不一致:整数返回数值区的上下限,浮点数返回最小绝对值和上限。
cout << numeric_limits<short>::min() << endl; //-32768cout << numeric_limits<short>::max() << endl; //32767cout << numeric_limits<unsigned short>::min() << endl; //0cout << numeric_limits<unsigned short>::max() << endl; //65535cout << numeric_limits<float>::min() << endl; //1.17549e-38cout << numeric_limits<float>::max() << endl; //3.40282e+38
这种不一致导致在编写泛型代码必须手工区分数值类型,容易混淆概念
bounds弥补了numeric_limits的缺点,它用三个成员函数明确给出了数值类型的最大极值、最小极值和最小正规值
template<class N>struct bounds{ static N lowest(); //最小极值,下界 static N highest(); //最大极值,上界 static N smallest(); //最小正规值};
using namespace boost::numeric;cout << bounds<short>::lowest() << endl;cout << bounds<short>::highest() << endl;cout << bounds<short>::smallest() << endl;cout << bounds<float>::lowest() << endl;cout << bounds<float>::highest() << endl;cout << bounds<float>::smallest() << endl;assert(bounds<short>::lowest() == numeric_limits<short>::min());assert(bounds<float>::lowest() == -numeric_limits<float>::max());
C++11修订了numeric_limits,使用constexpr关键字令所有函数都成为编译期常量,并且新增了lowest()函数,相当于bounds的lowest(),其他max()和min()没有变化。
numeric_cast
numeric_cast()函数可以在转型时进行范围检查,如果超出范围就抛出异常std::bad_cast,比static_cast安全。numeric_cast位于名字空间boost,需要头文件<boost/numeric/conversion/cast.hpp>
short s = bounds<short>::highest();int i = boost::numeric_cast<int>(s);assert(i == s);try{ char c = boost::numeric_cast<char>(s);}catch (std::bad_cast& e){ cout << e.what() << endl;}
- Boost组件实用工具
- Boost实用工具
- boost 实用工具
- boost 库实用工具之optional
- boost库实用工具之assign
- boost实用工具之tribool库
- boost库实用工具之exception
- Boost 库中的实用工具类使用入门
- Boost 库中的实用工具类使用入门
- Boost 库中的实用工具类使用入门
- Boost 库中的实用工具类使用入门
- Boost 库中的实用工具类使用入门
- Boost 库中的实用工具类使用入门
- Boost 库中的实用工具类使用入门
- Boost 库中的实用工具类使用入门
- Boost 库中的实用工具类使用入门
- Boost库中的实用工具类使用入门
- Boost 库中的实用工具类使用入门
- CHAPTER 14 -Recurrent Neural Networks
- undefined reference to `__aeabi_uidivmod'和undefined reference to `__aeabi_uidiv'错误
- 理解Android Binder架构
- JAVA集合
- 关于等价鞅、反等价鞅、剀利公式、赌徒输光定理(非常有启发意义)
- Boost组件实用工具
- SQLServer中Partition By 函数的使用
- Asset Catalog Compiler Warning Group
- lintcode--上一个排列
- Java基础学习之接口
- Android Studio 使用config.gradle 管理依赖库
- PHP的一个猜拳小游戏
- sql server 2005 通过存储过程发送邮件
- leetcode33题解_Search in Rotated Sorted Array