您的位置:首页 > 其它

boost::any实现分析

2013-03-19 17:09 197 查看
这里提到的boost::any,下面来分析一下boost::any的实现。

class any
{
public: // structors

any()
: content(0)
{
}

template<typename ValueType>
any(const ValueType & value)
: content(new holder<ValueType>(value))
{
}

any(const any & other)
: content(other.content ? other.content->clone() : 0)
{
}

~any()
{
delete content;
}

public: // modifiers

any & swap(any & rhs)
{
std::swap(content, rhs.content);
return *this;
}

template<typename ValueType>
any & operator=(const ValueType & rhs)
{
any(rhs).swap(*this);
return *this;
}

any & operator=(any rhs)
{
rhs.swap(*this);
return *this;
}

public: // queries

bool empty() const
{
return !content;
}

const std::type_info & type() const
{
return content ? content->type() : typeid(void);
}

#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
private: // types
#else
public: // types (public so any_cast can be non-friend)
#endif

class placeholder
{
public: // structors

virtual ~placeholder()
{
}

public: // queries

virtual const std::type_info & type() const = 0;

virtual placeholder * clone() const = 0;

};

template<typename ValueType>
class holder : public placeholder
{
public: // structors

holder(const ValueType & value)
: held(value)
{
}

public: // queries

virtual const std::type_info & type() const
{
return typeid(ValueType);
}

virtual placeholder * clone() const
{
return new holder(held);
}

public: // representation

ValueType held;

private: // intentionally left unimplemented
holder & operator=(const holder &);
};

#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS

private: // representation

template<typename ValueType>
friend ValueType * any_cast(any *);

template<typename ValueType>
friend ValueType * unsafe_any_cast(any *);

#else

public: // representation (public so any_cast can be non-friend)

#endif

placeholder * content;

};


首先,any类没有用模版参数,这是为什么呢?显而易见,因为any的设计初衷我想就是提供如下的使用方式:

boost::any any_value(10);
boost::any any_value1("123");


而非

boost::any<int> any_value(10);
boost::any<std::string> any_value1("123");


即,提供一种动态的数据类型,并接受单个对象的同时能够获得它的类型

如果用了模版参数变成boost::any<int> any_value(10)这样,那any_value就确定是int类型,并且在使用时还需要传类型作为模版参数。所以,any必须不能是模板类。那么问题来了,如果any不是模板类,它怎么才能泛化的保存各种类型呢?答案就在虚基类placeholder和它的派生模板类holder。

因为any不是模板类不能保存泛型数据,那就需要将原数据用一种容器包起来,然后让any持有一个指针,指向这个容器。那么很明显,这个容器类必须是模板类,因为这样它才能保存泛型数据,而又因为any不是模板类,它的任何数据成员都不能是泛型的,那就需要一个基类,让any持有这个基类的指针,而“容器”从这个基类派生并带模版参数。这样就解决了问题,即让原来需要泛型化存储数据的的any不泛型化,转而定义一种泛型化容器来保存数据,而any自己握住该泛型化容器基类的指针指向这个泛型化的容器。太绕了...绕这么一圈的目的,就是为了开始提到的涉及初衷:提供一种动态的数据类型,并接受单个对象的同时能够获得它的类型。

核心的分析完了,再分析些具体的。

既然需要

boost::any any_value(10);
boost::any any_value1 = "str";


那就需要对any的带参构造函数、拷贝构造函数处理成带模版参数的,并重载=操作符(也带模版参数)。这里可以仔细看看重载=操作符里的swap函数,很多重载=操作符的都是采用swap的方式实现赋值的。

template<typename ValueType>
any & operator=(const ValueType & rhs)
{
any(rhs).swap(*this);
return *this;
}
这里先创建一个临时对象any(rhs),再调用swap进行底层数据交换,注意与*this交换数据的是临时对象,所以rhs的底层数据并未更改,在swap结束后临时对象拿住了*this的底层数据,而这时*this也拿到了临时对象构造时创建的rhs的数据副本。临时对象由于函数结束生命周期也结束而析构掉,则原来*this的底层数据也就没了。这样就实现了将*this原来的底层数据干掉,让*this持有rhs的底层数据的副本。

再说下从any对象中获取数据的方式

int a = 0;
boost::any any_value(a);
int b = any_value;//这是不允许的
int b = any_cast<int>(any_value);//正确


直接将any对象赋给某种类型的对象是不允许的,编不过,因为any对象并没有重载目标对象的操作符(也不可能重载,那么多类型)。因此就需要采用any_cast的方式,当然,还有一个unsafe_any_cast的函数,差别就是any_cast在转换时会进行类型匹配检查,只有类型一致的,才会转,否则抛出异常,而unsafe_any_cast不进行类型匹配检查而直接转。

代码如下:

class bad_any_cast : public std::bad_cast
{
public:
virtual const char * what() const throw()
{
return "boost::bad_any_cast: "
"failed conversion using boost::any_cast";
}
};

template<typename ValueType>
ValueType * any_cast(any * operand)
{
return operand &&
#ifdef BOOST_AUX_ANY_TYPE_ID_NAME
std::strcmp(operand->type().name(), typeid(ValueType).name()) == 0
#else
operand->type() == typeid(ValueType)
#endif
? &static_cast<any::holder<ValueType> *>(operand->content)->held
: 0;
}

template<typename ValueType>
inline const ValueType * any_cast(const any * operand)
{
return any_cast<ValueType>(const_cast<any *>(operand));
}

template<typename ValueType>
ValueType any_cast(any & operand)
{
typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref;

#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
// If 'nonref' is still reference type, it means the user has not
// specialized 'remove_reference'.

// Please use BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION macro
// to generate specialization of remove_reference for your class
// See type traits library documentation for details
BOOST_STATIC_ASSERT(!is_reference<nonref>::value);
#endif

nonref * result = any_cast<nonref>(&operand);
if(!result)
boost::throw_exception(bad_any_cast());
return *result;
}

template<typename ValueType>
inline ValueType any_cast(const any & operand)
{
typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref;

#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
// The comment in the above version of 'any_cast' explains when this
// assert is fired and what to do.
BOOST_STATIC_ASSERT(!is_reference<nonref>::value);
#endif

return any_cast<const nonref &>(const_cast<any &>(operand));
}

// Note: The "unsafe" versions of any_cast are not part of the
// public interface and may be removed at any time. They are
// required where we know what type is stored in the any and can't
// use typeid() comparison, e.g., when our types may travel across
// different shared libraries.
template<typename ValueType>
inline ValueType * unsafe_any_cast(any * operand)
{
return &static_cast<any::holder<ValueType> *>(operand->content)->held;
}

template<typename ValueType>
inline const ValueType * unsafe_any_cast(const any * operand)
{
return unsafe_any_cast<ValueType>(const_cast<any *>(operand));
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: