1,它本身不能是模板类,因为如果它是模板,你必须为它的具现化提供模板参数。这样就不能让同一个对象接受任意类型的数据。
2, 容纳任意类型原因:构造函数和赋值函数,均是模板函数,可以接受任意的类型,将值存入内部的模板类holder中。因此,any实际上是一个包装类。
3,智能指针的选择:auto_ptr,scoped_ptr不能放入容器
shared_ptr可以放入容器
vector<any>v;
v.push_back(10);
v.push_back(10.0);
v=list_of<any>(10)("ss")
5,实现虚模板函数(注:C++不允许存在)
1,首先,any类里面一定要提供一个模板构造函数和模板operator=操作符。
2,为容器准备一个非泛型的基类,而让指针指向该基类【数据不能将它保存在any类中,那会导致any类成为模板类】
3, 源码
class any
{
public:
class placeholder // 泛型数据容器holder的非泛型基类
{
public:
// 虚析构函数,为保证派生类对象能用基类指针析构
virtual~placeholder(){}
public:
// 提供关于类型的信息
virtual conststd::type_info & type() const = 0;
virtualplaceholder * clone() const = 0; // 复制
}; // placeholder
template<typename ValueType>
class holder :public placeholder
{
public:
holder(constValueType & value)
: held(value)
{}
public:
virtual conststd::type_info & type() const
{
returntypeid(ValueType);
}
virtualplaceholder * clone() const
{
return newholder(held); // 改写虚函数,返回自身的复制体
}
public:
ValueType held; // 数据保存的地方
}; // holder
// 指向泛型数据容器holder的基类placeholder的指针
placeholder * content;
//模板构造函数,动态分配数据容器并调用其构造函数
template<typename ValueType>
any(const ValueType & value)
: content(newholder<ValueType>(value))
{}
...
//与模板构造函数一样,但使用了swap惯用手法
template<typename ValueType>
any & operator=(const ValueType& rhs)
{
//先创建一个临时对象any(rhs),注意与*this交换数据的是临时对象
临时对象由于生命期的结束而被自动析构,*this原来的底层数据随之烟消云散。
any(rhs).swap(*this);
return *this;
}
~any() //析构函数
{
//释放容器,用的是基类指针,这就是placeholder需要一个虚析构函数的原因
delete content;
}
...
};
1,
template<typename ValueType>
ValueType any_cast(const any &operand)
{
// 调用any_cast针对指针的版本。
const ValueType * result =any_cast<ValueType>(&operand);
// 如果cast失败,即实际 保存的并非ValueType型数据,则抛出一个异常。
if(!result)
throwbad_any_cast(); // 派生自std::bad_cast
return *result;
},
2,针对指针的版本,static_cast实现
template<typename ValueType>
ValueType * any_cast(any * operand)
{
// 这个类型检查很重要,后面会对它作更详细的解释
return
operand&&
(operand->type()==typeid(ValueType)) ? // #1
&static_cast<any::holder<ValueType>*>(operand->content)->held
: 0;// 这儿有个向下类型转换
}
使用:
int i=10;
boost::any anyVal=i;
double d=any_cast<double>(anyVal);
1, boost::anyVal=i;其实将anyVal.content指针指向了一个holder<int>对象
2, 然后any_cast<double>(anyVal)实际上调用了any_cast<>针对指针的重载版本,并将anyVal的地址传递过去,也就是转到#1处
3, #1处的代码被编译器实例化为: #2
static_cast<any::holder<double>*>(operand->content)->held
operand->content实际指向的是any::holder<int>
原来成员分配了sizeof(int)个字节的内存,而现在却要将int型的held当作double型来使用,也就是说使用sizeof(double)个字节内存。
错误写法:
int i=10;
boost::anyanyVal=i;
int j=anyVal;
原因:
因为事先并不知道要用anyVal来承载何种类型的变量,所以转换操作符无从给出。
目前的C++标准不允许模板函数为虚函数,any可以一定程度上满足这种需要,例如,
class Base
{
public:
virtual voidAccept(boost::any anyData)
{
...
}
};
class Derived:public Base
{
public:
virtual voidAccept(boost::any anyData)
{
...
}
};
这样的Accept函数能够接受任意类型的数据,并且是virtual函数。
【虚函数是为了实现运行时调用函数的晚绑定,是通过虚拟表是实现的,在编译时添加了Vtable指针。
而模板是为了编程的通用化而设计的,所以它是在编译时根据模板类型进行类型替换。】
当前的编译器都期望在处理类的定义的时候就能确定这个类的虚函数表的大小,如果允许有类的虚成员模板函数,那么就必须要求编译器提前知道程序中所有对该类的该虚成员模板函数的调用,而这是不可行的。
即对于一个模板函数,不同的模板参数会产生出不同的函数。这样的话,如果要知道类内包含多少个虚函数,就只能去代码中寻找。这就不单单是多文件的问题,还有RTTI的问题了。
template< typename T >
class A
{
public:
virtual void B() = 0;
};
template< typename T >
class C : public A< T >
{
public:
void B()
{
}
};
template< typename T >
class D : public A< T >
{
public:
void B()
{
}
};