- 无限制的类型,如 void*. 这种方法不可能是类型安全的,应该象逃避灾难一样避免它。
- 可变的类型,即支持多种类型的存储和获取的类型。
- 支持转换的类型,如字符串类型与整数类型之间的转换。
Any 库如何改进你的程序
任意类型的类型安全存储以及安全的取回
在标准库容器中存放不同类型的方法
可以在无须知道类型的情况下传送类型
Any库提供一个类型, any, 它允许存入任意类型且稍后取回,而不损失类型安全性。它有点象是可变类型的化合物:它可以持有任意类型,但你必须知道类型才能取回值。有很多次你想在同一个容器中存入互不相关的类型。有很多次某些代码只想从一个指针向另一个指针传送数据,而不关心数据的类型。从表面看,这些事情很容易做。它们可以通过一个无类的类型来实现,如 void*. 它们也可以通过使用一个含有不同类型的union来实现。有很多可变类型通过一些类型标识机制来实现。不幸的是,所有这些都缺乏类型安全性,而只有在最可控的情形下我们才应该故意绕过类型系统。标准库的容器是要通过它们所包含的类型来特化的,这意味着不可能把不同类型的元素存入容器之内。幸运的是,解决的方案不一定要 void*, 因为 Any 库允许你将存入不同的类型而稍后取回。没有办法在不知道实际类型的情况下取回存入的值,类型安全从而得到保证。
在设计框架时,不可能预先知道哪些类型要和框架类一起使用。一个常见的方法是,要求框架的使用者遵守某种接口,或者从框架所提供的某个基类进行派生。这是合理的,因为框架可能需要与不同的高级类进行通信才能使用。但是也存在这样的情形,框架对于存入或接受的类型无须(或不能)知道任何相关信息。不要绕过类型系统去使用 void* 方法,框架可以使用 any 。
Any 如何适用于标准库
Any的一个重要特性是,它提供了存储不同类型的对象到标准库容器中的能力。它也是一种可变数据类型,这正是C++标准库非常需要而又缺乏的。
- CopyConstructible 它必须可以复制这个类型
- Non-throwing destructor 就象所有析构函数应该的那样!
- Assignable 为了保证强异常安全(不符合可赋值要求的类型也可以用于 any, 但没有强异常安全的保证)
namespace boost {
class any {
public:
any();
any(const any&);
template<typename ValueType>
any(const ValueType&);
~any();
any& swap(any &);
any& operator=(const any&);
template<typename ValueType>
any& operator=(const ValueType&);
bool empty() const;
const std::type_info& type() const;
};
}当试图将一个any对象转换为该对象所存类型以外的其它类型,将抛出该异常。bad_any_cast 派生自 std::bad_cast. 注意,使用指针参数调用 any_cast 时,将不抛出异常(类似于对指针使用 dynamic_cast 时返回空指针一样),反之对引用类型使用 dynamic_cast 则会在失败时抛出异常。
#include <iostream>
#include <string>
#include <utility>
#include <vector>
#include <boost/any.hpp>
using namespace std;
struct A
{
void some_function() { cout << "A::some_function()" << endl; }
};
struct B
{
void some_function() { cout << "B::some_function()" << endl; }
};
struct C
{
void some_function() { cout << "C::some_function()" << endl; }
};
void print_any(boost::any& a)
{
if (A* pA=boost::any_cast<A>(&a))
{
pA->some_function();
}
else if (B* pB=boost::any_cast<B>(&a))
{
pB->some_function();
}
else if (C* pC=boost::any_cast<C>(&a))
{
pC->some_function();
}
else
{
try
{
cout << boost::any_cast<string>(a) << endl;
}
catch(boost::bad_any_cast &e)
{
cout << "Oops~" << e.what() << endl;
}
}
}
int main()
{
std::vector<boost::any> store_anything;
store_anything.push_back(A());
store_anything.push_back(B());
store_anything.push_back(C());
store_anything.push_back(string("This is fantastic! "));
store_anything.push_back(3);
store_anything.push_back(make_pair(true, 7.92));
for_each( store_anything.begin(),
store_anything.end(),
print_any);
}B::some_function()
C::some_function()
This is fantastic!
Oops~boost::bad_any_cast: failed conversion using boost::any_cast
Oops~boost::bad_any_cast: failed conversion using boost::any_cast
#include <iostream>
#include <string>
#include <boost/any.hpp>
using namespace std;
int main()
{
boost::any a1(100);
boost::any a2(std::string("200"));
boost::any a3;
cout << "a3 is " << (a3.empty() ? "empty" : "not empty" )<< endl;
a1.swap(a2);
try
{
string s=boost::any_cast<std::string>(a1);
cout << "a1 contains a string: " << s << endl;
}
catch(boost::bad_any_cast& e)
{
std::cout << "a1 doesn't contain a string! " << e.what() << endl;
}
if (int* p=boost::any_cast<int>(&a2))
{
cout << "a2 seems to have swapped contents with a1: " << *p << endl;
}
else
{
cout << "Nope, no int in a2" << endl;
}
if (typeid(int)==a2.type())
{
cout << "a2's type_info equals the type_info of int" << endl;
}
}a1 contains a string: 200
a2 seems to have swapped contents with a1: 100
a2's type_info equals the type_info of int
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <boost/any.hpp>
#include <boost/shared_ptr.hpp>
using namespace std;
class A
{
private:
string m_str;
public:
A(string str):m_str(str){}
virtual ~A() {cout << "A::~A()---" << m_str << endl; }
void not_virtual() {cout << "A::not_virtual()---" << m_str << endl;}
virtual void is_virtual () {cout << "A:: is_virtual ()---" << m_str<< endl;}
};
class B : public A
{
private:
string m_str;
public:
B(string str):A(str),m_str(str){}
~B(){cout << "B::~B()---" << m_str << endl;}
void not_virtual() {cout << "B::not_virtual()---" << m_str << endl;}
virtual void is_virtual () {cout << "B:: is_virtual ()---"<< m_str << endl;}
};
void foo(boost::any& a)
{
try
{
boost::shared_ptr<A> ptr = boost::any_cast<boost::shared_ptr<A> >(a);
ptr-> is_virtual ();
ptr->not_virtual();
return;
}
catch(boost::bad_any_cast& e) {}
try
{
boost::shared_ptr<B> ptr = boost::any_cast<boost::shared_ptr<B> >(a);
ptr-> is_virtual ();
ptr->not_virtual();
return;
}
catch(boost::bad_any_cast& e) {}
cout << "~~~~~~~other types~~~~~~~~" << endl;
}
int main()
{
boost::any a1(boost::shared_ptr<A>(new A("a1")));
boost::any a2(string("Just a string"));
{
boost::any b1(boost::shared_ptr<A>(new B("b1")));
boost::any b2(boost::shared_ptr<B>(new B("b2")));
vector<boost::any> vec;
vec.push_back(a1);
vec.push_back(a2);
vec.push_back(b1);
vec.push_back(b2);
for_each(vec.begin(),vec.end(),foo);
cout << endl;
}
}A::not_virtual()---a1
~~~~~~~other types~~~~~~~~
B:: is_virtual ()---b1
A::not_virtual()---b1
B:: is_virtual ()---b2
B::not_virtual()---b2
B::~B()---b2
A::~A()---b2
B::~B()---b1
A::~A()---b1
A::~A()---a1
第三个元素含有一个指向 B 实例的 shared_ptr<A> 。这个例说明了 any 如何与其它类型一样实现多态性。当然,如果我们使用裸指针,就需要用 static_cast 来保存指针为我们想标识的类型。注意,函数 A::not_virtual 被调用而不是 B::not_virtual. 原因是这个指针的静态类型是 A*, 而不是 B*.
在里面的那个作用域结束时,vector 被销毁,它又销毁了内含的 any 实例,后者再销毁所有的 shared_ptr,正确地设置引用参数为零。接着,我们的指针被安全和不费力气地销毁!
这个例子显示了一些比如何与 any 一起使用智能指针更为重要的东西;它显示了存入 any 的类型有是简单的或是复杂的都无关紧要。如果复制被存值的代价是高得惊人的,或者如果需要共享使用和控制生存期,就应该考虑使用智能指针,就象使用标准库的容器保存值一样。同样的推理也适用于 any, 通常这两个原则是一致的,正如在容器中使用 any 就意味着要保存不同的类型。
在以下情形时使用 Any 库:
- 你需要在容器中存放不同类型的值
- 需要保存未知类型
- 类型被传递到无须知晓任何有关该类型信息的层次
在标准库中,有很好的工具来存放多个元素。当需要存储异类的元素时,我们想避免使用新的集合类型。any 提供了一种方法,在大多数情况下它可以与已有容器一起使用。在某种程度上,模板类 any 扩展了标准库容器的能力,把不同的类型封入一个同类型的包装中,就可以把它们放入前述容器中了。把 Boost.Any 加到已有代码中是很简单的。它不需要修改设计,并且立即就增加了灵活性。接口非常小,这使得它成为一个很容易理解的工具。
2458



被折叠的 条评论
为什么被折叠?



