boost源码剖析之:泛型指针类any之海纳百川(rev#2)
刘未鹏
C++的罗浮宫(http://blog.csdn.net/pongba)
动机
C++是强类型语言,所有强类型语言对类型的要求都是苛刻的,类型一有不合编译器就会抱怨说不能将某某类型转换为某某类型,当然如果在类型之间提供了转换操作符或是标准所允许的一定程度的隐式转换(如经过非explicit构造函数创建临时变量的隐式转换或是在int,long这些基本类型间的)又另当别论。总的说来,为了保持类型安全,C++有严厉的要求。然而有时候程序员可能有这样的需要:
int i;
iong j;
X x; // 假设X为用户定义的类
any anyVal=i;
... //use anyVal as a int value
anyVal=j;
... //use anyVal as a long value
anyVal=x;
... //use anyVal as a long value
考虑这样的一个“泛型指针类”该如何设计是很有趣的事情。
1. 它本身不能是模板类,因为如果它是模板,你必须为它的具现化提供模板参数。而事实上你并不想这样做。你想让同一个对象接受任意类型的数据。在上面的代码中这个对象是anyVal。然而,如果你必须为它提供模板参数,那么上面的代码看起来就会像这样:
any<int> anyIntVal=i;
any<long> anyLongVal=j;
...
这显然已经丧失了anyVal的优势——以单个对象接受所有类型的数据。与其这样还不如直接写:
int anyIntVal=i;
int anyLongVal=j;
所以,any不能是模板类。
2. 它必须提供某些有关它所保存的对象类型的信息。
3. 它必须提供某种方法将它保存的数值“取出来”。
事实上,boost库已经提供了这样的类boost::any,下面我就为你讲述它的原理及构造。
boost::any原理与结构
首先,any类里面一定要提供一个模板构造函数和模板operator=操作符。因为你必须允许用户写出:
any any_value(val); //val 的类型为任意的
any_value=val1; //val1 类型也是任意的
这样的代码。
其次,数据的存放之所是个问题,显然你不能将它保存在any类中,那会导致any类成为模板类,后者是明确不被允许的。数据应该动态存放,即动态分配一个数据的容器来存放数据,而any类中则保存指向这个容器的指针,明确地说,是指向这个容器的基类的指针,这是因为容器本身必须为模板,而any类中的指针成员又必须不是泛型的(因为any不能是泛型的,所以any中所有数据成员都不能是泛型的),所以,结论是:为容器准备一个非泛型的基类,而让指针指向该基类。
下面就看一看boost库是如何具体实现这两点的。
摘自”boost/any.hpp”
class any
{
public:
class placeholder // 泛型数据容器holder的非泛型基类
{
public:
// 虚析构函数,为保证派生类对象能用基类指针析构
virtual ~placeholder(){}
public:
// 提供关于类型的信息
virtual const std::type_info & type() const = 0;
virtual placeholder * clone() const = 0; // 复制
}; // placeholder
template<typename ValueType>
class holder : public placeholder
{
public:
holder(const ValueType & value)
: held(value)
{}
public:
virtual const std::type_info & type() const
{
// typeid返回std::typeinfo对象引用,后者包含任意对象的类型信息, 如name,此外还提供operator==操作符你可以用typeid(oneObj)==typeid(anotherObj)来比两个对象之类型是否一致。
return typeid(ValueType);
}
virtual placeholder * clone() const
{
return new holder(held); // 改写虚函数,返回自身的复制体
}
public:
ValueType held; // 数据保存的地方
}; // holder
// 指向泛型数据容器holder的基类placeholder的指针
placeholder * content;
//模板构造函数,动态分配数据容器并调用其构造函数
template<typename ValueType>
any(const ValueType & value)
: content(new holder<ValueType>(value))
{}
...
// 与模板构造函数一样,但使用了swap惯用手法
template<typename ValueType>
any & operator=(const ValueType & rhs)
{
// 先创建一个临时对象any(rhs),再调用下面的swap函数进行底层数据交换,注意与*this交换数据的是临时对象,所以rhs的底层数据并未被更改,只是在swap结束后临时对象拥有了*this的底层数据,而此时*this也拥有了临时对象构造时所拥有的rhs的数据的副本。然后临时对象由于生命期的结束而被自动析构,*this原来的底层数据随之烟消云散。
any(rhs).swap(*this);
return *this;
}
any & swap(any & rhs) //swap函数,交换底层数据
{
std::swap(content, rhs.content); // 只是简单地将两个指针的值互换
return *this;
}
~any() //析构函数
{
//释放容器,用的是基类指针,这就是placeholder需要一个虚析构函数的原因
delete content;
}
...
};
这虽然并非any的全部源代码,但是所有重要的思想已经表露无遗。剩下的部分只是一些简单的细节,请参见boost库的原文件。
“但是等等!”,你急切的说:“你失去了类型的信息。”唔...的确,当赋值的模板函数返回后你也就失去了关于类型的信息。考虑下面你可能想要写出的代码:
int i=10;
boost::any anyVal=i;
int j=anyVal;
// error,实际上你是想把anyVal赋给另一个int型变量,这应该以某种方式被允许,但决不是在any类中提供转换操作符,因为你事先并不知道要用anyVal来承载何种类型的变量,所以转换操作符无从给出。
当转换操作符的设想彻底失败后,我们只能借助于某些“外来”的显式转换操作。就向static_cast<>一样。boost提供了any_cast<>,于是你可以这样写:
int j=any_cast<int>(anyVal);
事实上,any_cast的代码是这样的:
template<typename ValueType>
ValueType any_cast(const any & operand)
{
// 调用any_cast针对指针的版本。
const ValueType * result = any_cast<ValueType>(&operand);
// 如果cast失败,即实际 保存的并非ValueType型数据,则抛出一个异常。
if(!result)
throw bad_any_cast(); // 派生自std::bad_cast
return *result;
}
而any_cast针对指针的版本是这样:
template<typename ValueType>
ValueType * any_cast(any * operand)
{
// 这个类型检查很重要,后面会对它作更详细的解释
return
operand &&
(operand->type()==typeid(ValueType)) ? // #1
border-right: medium none; padding-right: 0cm;
分享到:
相关推荐
C++设计新思维:泛型编程与设计模式之应用.pdf 带目录书签
《C++设计新思维:泛型编程与设计模式之应用》正是这样一本书,它不仅深入探讨了C++的核心理念,还着重强调了泛型编程与设计模式的实际应用,为开发者提供了一个新的视角来审视和掌握这门语言。 泛型编程是C++设计...
《C++设计新思维:泛型编程与设计模式之应用》这本书深入探讨了C++语言在泛型编程和设计模式中的应用,对于理解和提升C++编程能力有着重要的指导价值。以下将围绕这些主题展开详细讨论。 一、泛型编程 泛型编程是...
C++ 设计新思维:泛型编程与设计模式之应用C++ 设计新思维:泛型编程与设计模式之应用C++ 设计新思维:泛型编程与设计模式之应用C++ 设计新思维:泛型编程与设计模式之应用C++ 设计新思维:泛型编程与设计模式之应用
2. 泛型类:泛型类是在类定义中包含一个或多个类型参数的类。例如,`List<T>`就是C#中最常见的泛型类,它可以存储任何类型的数据。通过使用泛型类,我们可以创建一个通用的数据结构,而无需为每种数据类型创建单独的...
【VC++ 2005:泛型编程】 泛型编程是C++/CLI中的一种重要特性,允许程序员创建可重用的代码,这些代码能够处理多种数据类型,而无需每次都复制和修改代码。泛型编程的核心思想是参数化类型,即将数据类型作为一个...
泛型+反射:泛型 笔记 ,课后作业
源码文件 `<反射(2):泛型相关周边信息获取>` 可能包含示例代码,演示了如何通过反射获取和操作泛型类和接口的实例。通过阅读和学习这段代码,开发者可以更深入地理解反射和泛型的交互,提升在实际项目中的应用...
C++ 设计新思维:泛型编程与设计模式之应用(简体中文).pdf c++八大金刚: 1、Essentital c++---lippman---C++之父,旁枝暂略,主攻核心,轻薄短小,初学者 2、The c++ programming language----C++之父,技术...
编程选择题40道:泛型:类型安全与泛型编程.Tex.docx
泛型是Java编程语言中用于减少类型转换错误和增强代码安全性的机制,它允许在定义类、接口和方法时使用类型参数。通过这种方式,可以在编译时期捕获那些只有在运行时期才会暴露的类型错误,提高了代码的健壮性。 ...
C#中的泛型类是编程中的一个重要概念,它允许我们创建可以处理多种数据类型的类,提高了代码的重用性和效率。下面将详细讲解泛型类的各个方面。 1. **泛型类的基本应用** 泛型类的核心在于类型参数,这使得类能够...
泛型主要分为四个关键部分:泛型类、泛型方法、泛型接口和泛型委托。下面将详细介绍这四个方面。 1. 泛型类: 泛型类是具有一个或多个类型参数的类。这些类型参数是占位符,代表一种未知的数据类型,直到在创建类...
2. **泛型容器** VC++ 2005中的标准模板库(Standard Template Library, STL)提供了如`vector`, `list`, `map`等泛型容器。这些容器可以存储任何类型的元素,只要这些元素满足容器的基本要求,如大小可变、可比较...
在本节“VS2010轻松学习C# - 从零到深入 - 天轰穿.NET4趣味编程视频教程_第23讲:泛型入门”中,我们将深入探讨C#中的一个重要特性——泛型。泛型是.NET Framework 2.0引入的关键功能,它极大地提高了代码的灵活性和...