boost.any实现任意类型存储

当你需要一个可变的类型时,有三种可能的解决方案:

  1. 无限制的类型,如 void*. 这种方法不可能是类型安全的,应该象逃避灾难一样避免它。
  2. 可变的类型,即支持多种类型的存储和获取的类型。
  3. 支持转换的类型,如字符串类型与整数类型之间的转换。

Any实现了第二种方案,一个基于值的可变化的类型,无限可能的类型。这个库通常用于把不同类型的东西存储到标准库的容器中。

Any 库如何改进你的程序

任意类型的类型安全存储以及安全的取回

在标准库容器中存放不同类型的方法

可以在无须知道类型的情况下传送类型

Any库提供一个类型, any, 它允许存入任意类型且稍后取回,而不损失类型安全性。它有点象是可变类型的化合物:它可以持有任意类型,但你必须知道类型才能取回值。有很多次你想在同一个容器中存入互不相关的类型。有很多次某些代码只想从一个指针向另一个指针传送数据,而不关心数据的类型。从表面看,这些事情很容易做。它们可以通过一个无类的类型来实现,如 void*. 它们也可以通过使用一个含有不同类型的union来实现。有很多可变类型通过一些类型标识机制来实现。不幸的是,所有这些都缺乏类型安全性,而只有在最可控的情形下我们才应该故意绕过类型系统。标准库的容器是要通过它们所包含的类型来特化的,这意味着不可能把不同类型的元素存入容器之内。幸运的是,解决的方案不一定要
void*, 因为 Any 库允许你将存入不同的类型而稍后取回。没有办法在不知道实际类型的情况下取回存入的值,类型安全从而得到保证。

在设计框架时,不可能预先知道哪些类型要和框架类一起使用。一个常见的方法是,要求框架的使用者遵守某种接口,或者从框架所提供的某个基类进行派生。这是合理的,因为框架可能需要与不同的高级类进行通信才能使用。但是也存在这样的情形,框架对于存入或接受的类型无须(或不能)知道任何相关信息。不要绕过类型系统去使用 void* 方法,框架可以使用 any 。

Any 如何适用于标准库

Any的一个重要特性是,它提供了存储不同类型的对象到标准库容器中的能力。它也是一种可变数据类型,这正是C++标准库非常需要而又缺乏的。

Boost.Any

类 any 允许对任意类型进行类型安全的存储和取回。不象无类类型,any 保存了类型信息,并且不会让你在不知道正确类型的情况下获得存入的值。当然,有办法可以让你询问关于类型的信息,也有测试保存的值的方法,但最终,调用者必须知道在 any 对象中的值的真实类型,否则不能访问any。可以把 any 看作为上锁的安全性。没有正确的钥匙,你不能进入其中。any 对它所保存的类型有以下要求:

  1. CopyConstructible 它必须可以复制这个类型
  2. Non-throwing destructor 就象所有析构函数应该的那样!
  3. 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();

缺省构造函数创建一个空的 any 实例,即一个不含有值的 any。当然,你无法从一个空的any中取回值,因为没有值存在。

any(const any& other);

创建一个已有 any 对象的独立拷贝。other 中含有的值被复制并存入 this.

template<typename ValueType> any(const ValueType&);

这个模板构造函数存入一个传入的ValueType类型参数的拷贝。参数是一个 const 引用,因此传入一个临时对象来存入any是合法的。注意,该构造函数不是 explicit 的,如果是的话, any 会难以使用,而且也不会增加安全性。

~any();

析构函数销毁含有的值,但注意,由于对一个裸指针的析构不会调用operator delete 或 operator delete[] ,所以在any中使用指针时,你应该把裸指针包装成一个象 shared_ptr 那样的智能指针。

any& swap(any& other);

交换存在两个 any 对象中的值。

any& operator=(const any& other);

如果any实例非空,则丢弃所存放的值,并存入other值的拷贝。

template<typename ValueType>  any& operator=(const ValueType& value);

如果any实例非空,则丢弃所存放的值,并存入 value 的一份拷贝,value可以是任意符合any要求的类型。

bool empty() const;

给出any实例当前是否有值,不管是什么值。因而,当any持有一个指针时,即使该指针值为空,则 empty 也返回 false 。

const std::type_info& type() const;

给出所存值的类型。如果 any 为空,则类型为 void.

普通函数

template<typename ValueType>  ValueType any_cast(const any& operand);

any_cast 让你访问any中存放的值。参数为需要取回值的 any 对象。如果类型 ValueType 与所存值不符,any 抛出一个 bad_any_cast 异常。请注意,这个语法有点象 dynamic_cast.

template<typename ValueType>  const ValueType* any_cast(const any* operand);

any_cast 的一个重载,接受一个指向 any 的指针,并返回一个指向所存值的指针。如果 any 中的类型不是 ValueType, 则返回一个空指针。请再次注意,这个语法也有点象 dynamic_cast.

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

any_cast 的另一个重载,与前一个版本相似,但前一个版本使用 const 指针的参数并返回 const 指针,这个版本则不是。

异常

bad_any_cast

当试图将一个any对象转换为该对象所存类型以外的其它类型,将抛出该异常。bad_any_cast 派生自 std::bad_cast. 注意,使用指针参数调用 any_cast 时,将不抛出异常(类似于对指针使用 dynamic_cast 时返回空指针一样),反之对引用类型使用 dynamic_cast 则会在失败时抛出异常。

用法

Any库定义在名字空间 boost 内。你要用类 any 来保存值,用模板函数 any_cast 来取回存放的值。为了使用 any, 要包含头文件 "boost/any.hpp". any 只允许你在知道类型的前提下访问它的值,这是很明智的。对于这个库,典型情况下你只需记住两件事:类 any, 用于存放值,还有模板函数 any_cast, 用于取回值。

在转型失败不是一种错误时,使用指针来传递 any, 但如果转型失败是一种错误,则应该使用const引用来传递,这样可以让 any_cast 在失败时抛出异常。如果你传递一个指针参数,你会得到一个指向保存值的指针;如果你传递一个 const 引用参数,你会得到一个保存值的拷贝。如果值的类型在拷贝时代价很昂贵,就应该传递指针以避免值的拷贝。

使用 any 让你能够在原来不能使用标准库容器和算法的地方下使用它们,从而让你可以写出更具有可维护性和更易懂的代码。

#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);
}

执行结果:

A::some_function()

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;
        }
}

执行结果:

a3 is empty

a1 contains a string: 200

a2 seems to have swapped contents with a1: 100

a2‘s type_info equals the type_info of int

保存指针

通常,测试 empty 足以知道对象是否真的包含有效的东西。但是,如果 any 持有的是一个指针,则要在解引用它之前额外小心地测试这个指针。仅仅简单地测试 any 是否为空是不够的,因为一个 any 在持有一个指针时会被认为是非空的,即使这个指针是空的。

在 any 中保存裸指针的另一个麻烦在于析构的语义。any 类接受了它所存值的所有权,因为它保持一个该值的内部拷贝,并与 any 一起销毁它。但是,销毁一个裸指针并不会对它调用 delete 或 delete[] !它仅仅要求归还属于指针的那点内存。这使得在 any 中保存指针是有问题的,所以更好的办法是使用智能指针来代替。的确,使用智能指针是在一个 any 中保存指针的好办法。它解决了要保证所存指针指向的内存被正确释放的问题。当智能指针被销毁时,它会正确地确保内存及其中的数据被正确销毁。作为对比,要注意
std::auto_ptr 不是合适的智能指针。这是因为 auto_ptr 没有通常的复制语义;访问一个 any 中的值会把内存及其中数据的所有权从 any 转移到返回的 auto_ptr 中。

#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:: is_virtual ()---a1

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

vector 中的含有 string 的 any 。这显示了保存一些对稍后要被调用的函数而言是未知类型的类型到一个 any,是很有可能的,通常也是合理的;这些函数只需要处理它们需要操作的类型!

第三个元素含有一个指向 B 实例的 shared_ptr<A> 。这个例说明了 any 如何与其它类型一样实现多态性。当然,如果我们使用裸指针,就需要用 static_cast 来保存指针为我们想标识的类型。注意,函数 A::not_virtual 被调用而不是 B::not_virtual. 原因是这个指针的静态类型是 A*, 而不是 B*.

在里面的那个作用域结束时,vector 被销毁,它又销毁了内含的 any 实例,后者再销毁所有的 shared_ptr,正确地设置引用参数为零。接着,我们的指针被安全和不费力气地销毁!

这个例子显示了一些比如何与 any 一起使用智能指针更为重要的东西;它显示了存入 any 的类型有是简单的或是复杂的都无关紧要。如果复制被存值的代价是高得惊人的,或者如果需要共享使用和控制生存期,就应该考虑使用智能指针,就象使用标准库的容器保存值一样。同样的推理也适用于 any, 通常这两个原则是一致的,正如在容器中使用 any 就意味着要保存不同的类型。

总结

这个类型可以包含不同类型的值,而且与无类类型(如 void*)有很大不同。我们总是严重地依赖C++中的类型安全,只有在极少数情形下我们会愿意没有它来干活。这是有很好的原因的:类型安全防止我们犯错,并改善了我们代码的性能。因此,我们应该避免无类类型。还有,发现自己需要异类存储的情形很少见,或者为了将使用者隔离于类型的细节,或者为了在更低的层次获得极度的灵活性。any 提供了这些功能,同时维护了类型安全,它是我们的工具箱的最好扩充!

在以下情形时使用 Any 库:

  1. 你需要在容器中存放不同类型的值
  2. 需要保存未知类型
  3. 类型被传递到无须知晓任何有关该类型信息的层次

Any 的设计同时也是一门很有价值的课程,关于如何封装一个类型而不影响到该类型的封套类。这种设计可以用于创建泛型函数对象、泛型迭代器等等。它是一个展示封装的威力以及与模板相关的多态性的例子。

在标准库中,有很好的工具来存放多个元素。当需要存储异类的元素时,我们想避免使用新的集合类型。any 提供了一种方法,在大多数情况下它可以与已有容器一起使用。在某种程度上,模板类 any 扩展了标准库容器的能力,把不同的类型封入一个同类型的包装中,就可以把它们放入前述容器中了。把 Boost.Any 加到已有代码中是很简单的。它不需要修改设计,并且立即就增加了灵活性。接口非常小,这使得它成为一个很容易理解的工具。

时间: 2024-10-18 16:26:38

boost.any实现任意类型存储的相关文章

runtime 任意类型 model 数据库方便存储

//这里边直接上代码 之后我在慢慢地讲解  之后我的QQ:378254160 我有DEMO 方便你们的使用联系我备注 runtime+数据库+任意model类型  当然有时候也是有局限的 //DataBasehandle.m #import "DataBasehandle.h" #import <objc/runtime.h> #import <sqlite3.h> //http://www.sjsjw.com/kf_mobile/article/4_17043

boost学习 内嵌类型 与 any 的代码联系

本文是学习 boost源码的一些练习 参考文章来自 刘未鹏 C++的罗浮宫(http://blog.csdn.net/pongba) 目录 http://blog.csdn.net/pongba/article/category/37521 检测内嵌类型 检测一个类中是否存在指定的类型 那么只需要利用模板检测输入参数 根据参数的不同 导入到不同的函数中 类似 template <typename T> void Register(T person) { Register(person, typ

编写高质量代码改善C#程序的157个建议——建议26:使用匿名类型存储LINQ查询结果

建议26:使用匿名类型存储LINQ查询结果 从.NET3.0开始,C#开始支持一个新特性:匿名类型.匿名类型有var.赋值运算符和一个非空初始值(或以new开头的初始化项)组成.匿名类型有如下基本特性: 即支持简单类型也指出复杂类型.简单类型必须是一个非空初始值,复杂类型则是一个以new开头的初始化项. 匿名类型的属性是只读的,没有属性设置器,它一旦被初始化就不可更改. 如果两个匿名类型的属性值相同,那么就认为这两个匿名类型相等. 匿名类型可以再循环中用作初始化器. 匿名类型支持智能感知. 匿名

谓松散类型就是指当一个变量被申明出来就可以保存任意类型的

量为松散类型,所谓松散类型就是指当一个变量被申明出来就可以保存任意类型的值,就是不像SQL一样申明某个键值为int就只能保存整型数值,申明varchar只能保存字符串.一个变量所保存值的类型也可以改变,这在JavaScript中是完全有效的,只是不推荐.相比较于将变量理 http://www.djob.com/job_8EED14ACE3DE31D0.htmlhttp://www.djob.com/job_59C4A0258B1285F2.htmlhttp://www.djob.com/job_

C# 任意类型数据转JSON格式

每天都是不一样,积极的去感受生活 C# 任意类型数据转JSON格式 /// <summary> /// List转成json /// </summary> /// <typeparam name="T"></typeparam> /// <param name="jsonName"></param> /// <param name="list"></para

Java使用JDBC连接任意类型数据库(mysql oracle。。)

package cn.liz.test; import java.io.InputStream; import java.sql.Connection; import java.sql.Driver; import java.sql.SQLException; import java.util.Properties; import org.junit.Test; public class JBDCtest { /** * 编写一个通用的方法, 在不修改源程序的情况下, 可以获取任何数据库的连接

任意类型排序

模拟qsort()函数,实现任意类型排序: qsort()简介: 原型: void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) base:要排序的的数组的首地址 num:要排序的元素的个数 width:元素所占的字节数 compare:函数指针,指向排序方式这个函数的指针.其中比较方式这个函数需要用户根据自己的需要 使用qso

是指存在继承关系的对象,不是任意类型的对象。当对不存在继承关系的对象进行强制类型转换时

这里所说的对象类型转换,是指存在继承关系的对象,不是任意类型的对象.当对不存在继承关系的对象进行强制类型转换时,java 运行时将抛出 java.lang.ClassCastException 异常.在继承链中,我们将子类向父类转换称为“向上转型”,将父类向子类转换称为“向下转型”.很多时候,我们会... Coda 发布于 2015-04-12 14:09 评论(1)阅读(72) 1 http://www.djob.com/job_8EED14ACE3DE31D0.htmlhttp://www.

1.实参和形参的关系 2.函数的参数可以是任意类型吗? 3.函数作用域的问题

1.实参和形参的关系 参数:定义函数的时候,小括号中的变量就是参数叫形参,在执行函数的时候小括号中的参数叫实参.实参可带可不带,带了就相当于给形参赋值,没带就是undefined.但是可以不带的实参,在定义函数的时候跟他对应的形参必须放在后面形参的个数可以大于实参的个数,反过来不行. 2.函数的参数可以是任意类型吗? 函数的参数可以是任意类型 3.函数作用域的问题 函数内部声明的变量都是私有变量.私有变量存到私有空间去.什么是私有变量函数中带var和function声明定义的变量是私有变量.函数