boost源码剖析之:泛型指针类any之海纳百川(rev#2)
刘未鹏
C++的罗浮宫(http://blog.csdn.net/pongba)
动机
C++是强类型语言,所有强类型语言对类型的要求都是苛刻的,类型一有不合编译器就会抱怨说不能将某某类型转换为某某类型,当然如果在类型之间提供了转换操作符或是标准所允许的一定程度的隐式转换(如经过非explicit构造函数创建临时变量的隐式转换或是在int,long这些基本类型间的)又另当别论。总的说来,为了保持类型安全,C++有严厉的要求。然而有时候程序员可能有这样的需要:
int i;
iong j;
X x; // 假设X为用户定义的类
any anyVal=i;
... //use anyVal as a int value
anyVal=j;
... //use anyVal as a long value
anyVal=x;
... //use anyVal as a long value
考虑这样的一个“泛型指针类”该如何设计是很有趣的事情。
1. 它本身不能是模板类,因为如果它是模板,你必须为它的具现化提供模板参数。而事实上你并不想这样做。你想让同一个对象接受任意类型的数据。在上面的代码中这个对象是anyVal。然而,如果你必须为它提供模板参数,那么上面的代码看起来就会像这样:
any<int> anyIntVal=i;
any<long> anyLongVal=j;
...
这显然已经丧失了anyVal的优势——以单个对象接受所有类型的数据。与其这样还不如直接写:
int anyIntVal=i;
int anyLongVal=j;
所以,any不能是模板类。
2. 它必须提供某些有关它所保存的对象类型的信息。
3. 它必须提供某种方法将它保存的数值“取出来”。
事实上,boost库已经提供了这样的类boost::any,下面我就为你讲述它的原理及构造。
boost::any原理与结构
首先,any类里面一定要提供一个模板构造函数和模板operator=操作符。因为你必须允许用户写出:
any any_value(val); //val 的类型为任意的
any_value=val1; //val1 类型也是任意的
这样的代码。
其次,数据的存放之所是个问题,显然你不能将它保存在any类中,那会导致any类成为模板类,后者是明确不被允许的。数据应该动态存放,即动态分配一个数据的容器来存放数据,而any类中则保存指向这个容器的指针,明确地说,是指向这个容器的基类的指针,这是因为容器本身必须为模板,而any类中的指针成员又必须不是泛型的(因为any不能是泛型的,所以any中所有数据成员都不能是泛型的),所以,结论是:为容器准备一个非泛型的基类,而让指针指向该基类。
下面就看一看boost库是如何具体实现这两点的。
摘自”boost/any.hpp”
class any
{
public:
class placeholder // 泛型数据容器holder的非泛型基类
{
public:
// 虚析构函数,为保证派生类对象能用基类指针析构
virtual ~placeholder(){}
public:
// 提供关于类型的信息
virtual const std::type_info & type() const = 0;
virtual placeholder * clone() const = 0; // 复制
}; // placeholder
template<typename ValueType>
class holder : public placeholder
{
public:
holder(const ValueType & value)
: held(value)
{}
public:
virtual const std::type_info & type() const
{
// typeid返回std::typeinfo对象引用,后者包含任意对象的类型信息, 如name,此外还提供operator==操作符你可以用typeid(oneObj)==typeid(anotherObj)来比两个对象之类型是否一致。
return typeid(ValueType);
}
virtual placeholder * clone() const
{
return new holder(held); // 改写虚函数,返回自身的复制体
}
public:
ValueType held; // 数据保存的地方
}; // holder
// 指向泛型数据容器holder的基类placeholder的指针
placeholder * content;
//模板构造函数,动态分配数据容器并调用其构造函数
template<typename ValueType>
any(const ValueType & value)
: content(new holder<ValueType>(value))
{}
...
// 与模板构造函数一样,但使用了swap惯用手法
template<typename ValueType>
any & operator=(const ValueType & rhs)
{
// 先创建一个临时对象any(rhs),再调用下面的swap函数进行底层数据交换,注意与*this交换数据的是临时对象,所以rhs的底层数据并未被更改,只是在swap结束后临时对象拥有了*this的底层数据,而此时*this也拥有了临时对象构造时所拥有的rhs的数据的副本。然后临时对象由于生命期的结束而被自动析构,*this