01.#include <iostream> 02.#include <list> 03.#include <boost/any.hpp> 04. 05.typedef std::list<boost::any> list_any; 06. 07.//关键部分:可以存放任意类型的对象 08.void fill_list(list_any& la) 09.{ 10. la.push_back(10);//存放常数 11. la.push_back( std::string("dyunze") );//存放字符串对象;注意la.push_back(“dyunze”)错误,因为会被当错字符串数组 12.} 13. 14.//根据类型进行显示 15.void show_list(list_any& la) 16.{ 17. list_any::iterator it; 18. boost::any anyone; 19. 20. for( it = la.begin(); it != la.end(); it++ ) 21. { 22. anyone = *it; 23. 24. if( anyone.type() == typeid(int) ) 25. std::cout<<boost::any_cast<int>(*it)<<std::endl; 26. else if( anyone.type() == typeid(std::string) ) 27. std::cout<<boost::any_cast<std::string>(*it).c_str()<<std::endl; 28. } 29.} 30. 31.int main() 32.{ 33. list_any la; 34. fill_list(la); 35. show_list(la); 36. 37. return 0; 38.} #include <iostream> #include <list> #include <boost/any.hpp> typedef std::list<boost::any> list_any; //关键部分:可以存放任意类型的对象 void fill_list(list_any& la) { la.push_back(10);//存放常数 la.push_back( std::string("dyunze") );//存放字符串对象;注意la.push_back(“dyunze”)错误,因为会被当错字符串数组 } //根据类型进行显示 void show_list(list_any& la) { list_any::iterator it; boost::any anyone; for( it = la.begin(); it != la.end(); it++ ) { anyone = *it; if( anyone.type() == typeid(int) ) std::cout<<boost::any_cast<int>(*it)<<std::endl; else if( anyone.type() == typeid(std::string) ) std::cout<<boost::any_cast<std::string>(*it).c_str()<<std::endl; } } int main() { list_any la; fill_list(la); show_list(la); return 0; } boost::any的优点: 对设计模式理解的朋友都会知道合成模式。因为多态只有在使用指针或引用的情况下才能显现,所以std容器中只能存放指针或引用(但实际上只能存放指针,无法存放引用,这个好像是c++的不足吧)。如: std::list<BaseClass*> mylist; 这样,我们就要对指针所指向内容的生存周期操心(可能需要程序员适时删除申请的内存;但是由于存放指针,插入/删除的效率高),而使用boost::any就可能避免这种情况,因为我们可以存放类型本身(当然存放指针也可以)。这是boost::any的优点之一。 boost::any另一个优点是可以存放任何类型。而前面提到的mylist只能存放BaseClass类指针以及其继承类的指针。 boost::any的缺点: 由于boost::any可以存放任何类型,自然它用不了多态特性,没有统一的接口,所以在获取容器中的元素时需要实现判别元素的真正类型,这增加了程序员的负担。与面向对象编程思想有些矛盾,但整个标准c++模板库何尝不是如此,用那些牛人的话来说,是“有益补充”。 总之,有利必有弊,没有十全十美的。 分析并模仿boost::any: 读了一下boost::any的源代码,并模仿一下其实现(相当一部分时拷贝原代码),下面是代码(只包含必要功能)。 实现any的功能主要由三部分组成: 1)any类 2)真正保存数据的holder类及其基类placeholder 3)获取真正数据的模板函数any_cast,类型转换的功能。 view plaincopy to clipboardprint? 01.#include <iostream> 02.#include <list> 03.#include <cassert> 04. 05.//自定义的any类 06.class any 07.{ 08.public: 09. 10. //保存真正数据的接口类 11. class placeholder 12. { 13. public: 14. virtual ~placeholder() 15. { 16. } 17. public: 18. 19. virtual const std::type_info & type() const = 0; 20. virtual placeholder * clone() const = 0; 21. }; 22. 23. //真正保存和获取数据的类。 24. template<typename ValueType> 25. class holder : public placeholder 26. { 27. public: 28. holder(const ValueType & value): held(value) 29. { 30. } 31. 32. public: 33. 34. virtual const std::type_info & type() const 35. { 36. return typeid(ValueType); 37. } 38. 39. virtual placeholder * clone() const 40. { 41. return new holder(held);//使用了原型模式 42. } 43. 44. public: 45. 46. //真正的数据,就保存在这里 47. ValueType held; 48. }; 49. 50.public: 51. 52. any(): content(NULL) 53. { 54. } 55. 56. //模板构造函数,参数可以是任意类型,真正的数据保存在content中 57. template<typename ValueType> 58. any(const ValueType & value): content(new holder<ValueType>(value)) 59. { 60. } 61. 62. //拷贝构造函数 63. any(const any & other) 64. : content(other.content ? other.content->clone() : 0) 65. { 66. } 67. 68. //析构函数,删除保存数据的content对象 69. ~any() 70. { 71. if(NULL != content) 72. delete content; 73. } 74. 75.private: 76. //一个placeholde对象指针,指向其子类folder的一个实现 77. // 即content( new holder<ValueType>(value) )语句 78. placeholder* content; 79. 80. template<typename ValueType> friend ValueType any_cast(const any& operand); 81.public: 82. 83. //查询真实数据的类型。 84. const std::type_info & type() const 85. { 86. return content ? content->type() : typeid(void); 87. } 88.}; 89. 90. 91.//获取content->helder数据的方法。用来获取真正的数据 92.template<typename ValueType> 93.ValueType any_cast(const any& operand) 94.{ 95. assert( operand.type() == typeid(ValueType) ); 96. return static_cast<any::holder<ValueType> *>(operand.content)->held; 97.} 98. 99.//下代码是使用示例 100. 101.typedef std::list<any> list_any; 102. 103.void fill_list(list_any& la) 104.{ 105. la.push_back(10);//存放常数;调用了any的模板构造函数,下同 106. la.push_back( std::string("我是string") );//存放字符串对象;注意la.push_back(“dyunze”)错误,因为会被当错字符串数组 107. 108. char* p = "我是常量区字符串abc"; 109. la.push_back(p);//可以存放指针,但要注意指针的失效问题 110.} 111. 112.//根据类型进行显示 113.void show_list(list_any& la) 114.{ 115. list_any::iterator it; 116. 117. for( it = la.begin(); it != la.end(); it++ ) 118. { 119. 120. if( (*it).type() == typeid(int) ) 121. std::cout<<any_cast<int>(*it)<<std::endl; 122. else if( (*it).type() == typeid(std::string) ) 123. std::cout<<any_cast<std::string>(*it).c_str()<<std::endl; 124. else if( (*it).type() == typeid(char*) ) 125. std::cout<<any_cast<char*>(*it)<<std::endl; 126. } 127.} 128. 129.int main() 130.{ 131. list_any la; 132. fill_list(la); 133. show_list(la); 134. 135. return 0; 136.} boost::any是一个很有趣的类,刚刚开始我还以为其就是一个variant类型, 能够将任意类型值保存进去,能够以任意类型值读出来,不过我错了 :( boost::any的作者认为,所谓generic type有三个层面的解释方法: 1.类似variant类型那样任意进行类型转换,可以保存一个(int)5进去, 读一个(string)"5"出来。Win下面的VARIANT类型是以一个巨大的 union实现的类似功能,使用灵活但效率较低 2.区别对待包含值的类型,保存一个(int)5进去,不会被隐式转换为 (string)'5'或者(double)5.0,这样效率较高且类型安全, 不必担心ambiguous conversions 3.对包含值类型不加区别,例如把所有保存的值强制转换为void *保存 读取时再有程序员判断其类型。这样效率虽最高但无法保证类型安全 boost::any就选择了第二层面的设计思路,它允许用户将任意类型值保存 进一个any类型变量,但内部并不改变值的类型,并提供方法让用户在使用时 主动/被动进行类型判断。 在实现方面,boost::any使用两层内部类placeholder和holder保存 实际类型的值。类placeholder只是一个接口,模板类holder是特定类型 的实现。其中type()方法获取实际值类型,即typeid(ValueType); clone()方法获取值的拷贝return new holder(held); virtual const std::type_info & type() const virtual placeholder * clone() const 其值的类型信息不象Win的VARIANT那样以专门的字段保存, 而是通过模板参数形式静态保存。这样效率更高(仅在编译期), 通用性更强(任何类型都可以,真正any)但灵活性不如VARIANT 在进行拷贝构造/赋值/swap时,都直接将整个placeholder换掉, 这样可以保证值类型的延续性。 在使用方面,提供了主动/被动进行类型检测的方法。 可以使用any::type()方法主动检测值类型 bool is_int(const boost::any & operand) { return operand.type() == typeid(int); } 也可以通过any_cast函数被动进行检测。此函数与C++中的*_cast 系列关键字有相同的语法规范,尝试进行类型转换,如类型不匹配则对 指针转换返回NULL,对引用转换抛出boost::bad_any_cast异常 boost::any str = string("12345"); try { cout << boost::any_cast<int>(str) << endl; } catch(boost::bad_any_cast e) { cerr << e.what() << endl; } 在应用方面,any类型适合于类型不同但使用相关的值。如C++的... 形式的函数参数本事不是类型安全的,可以通过vector<any>改造之 然后在使用时检测类型是否匹配,如可改造printf为 void safe_printf(const char *format, const vector<any>& params) { int index = 0; for(const char *pch = format; *pch; pch++) { switch(*pch) { case '%': { switch(*++pch) { case 'i': case 'd': { if(params[index].type() == typeid(int) || params[index].type() == typeid(short)) { ... } else throw ... } } } case '\': { ... } default: { putchar(*pch); } } } } 附:boost::any.hpp #ifndef BOOST_ANY_INCLUDED #define BOOST_ANY_INCLUDED // what: variant type boost::any // who: contributed by Kevlin Henney, // with features contributed and bugs found by // Ed Brey, Mark Rodgers, Peter Dimov, and James Curran // when: July 2001 // where: tested with BCC 5.5, MSVC 6.0, and g++ 2.95 #include <algorithm> #include <typeinfo> #include "boost/config.hpp" namespace boost { 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=(const any & rhs) { any(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; }; #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS private: // representation template<typename ValueType> friend ValueType * any_cast(any *); #else public: // representation (public so any_cast can be non-friend) #endif placeholder * content; }; 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 && operand->type() == typeid(ValueType) &static_cast<any::holder<ValueType> *>(operand->content)->held : 0; } template<typename ValueType> const ValueType * any_cast(const any * operand) { return any_cast<ValueType>(const_cast<any *>(operand)); } template<typename ValueType> ValueType any_cast(const any & operand) { const ValueType * result = any_cast<ValueType>(&operand); if(!result) throw bad_any_cast(); return *result; } } // Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved. // // Permission to use, copy, modify, and distribute this software for any // purpose is hereby granted without fee, provided that this copyright and // permissions notice appear in all copies and derivatives. // // This software is provided "as is" without express or implied warranty. #endif |
|