热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Boost源码剖析之:泛型指针类any之海纳百川

Boost源码剖析之:泛型指针类any之海纳百川作者:ppLiu(刘未鹏)C是强类型语言,所有强类型语言对型别的要求都是苛刻
Boost源码剖析之:泛型指针类any之海纳百川
作者:ppLiu(刘未鹏)

C++是强类型语言,所有强类型语言对型别的要求都是苛刻的,型别一有不合编译器就会抱怨说不能将某某型别转换为某某型别,当然如果在型别之间提供了转换操作符或是标准所允许的一定程度的隐式转换(如经过非explicit构造函数创建临时变量的隐式转换或是在int,long这些基本型别间的)又另当别论。总的说来,为了保持型别安全,C++有严厉的要求。然而有时候程序员可能有这样的需要:

i;
iong j;
X x;
any anyVal=i;
...
anyVal=j;
...
anyVal=x;
...

考虑这样的一个“泛型指针类”该如何设计是很有趣的事情。

1.它本身不能是模板类,因为如果它是模板,你必须为它的具现化提供模板参数。而事实上你并不想这样做。你想让同一个对象接受任意型别的数据。在上面的代码中这个对象是anyVal。然而,如果你必须为它提供模板参数,那么上面的代码看起来就会像这样:

any<> anyIntVal&#61;i;
any<> anyLongVal&#61;j;
...

这显然已经丧失了anyVal的优势----以单个对象接受所有型别的数据。与其这样还不如直接写:

anyIntVal&#61;i;anyLongVal&#61;j;

所以&#xff0c;any不能是模板类。

2&#xff0e;它必须提供某些有关它所保存的对象型别的信息。

3. 它必须提供某种方法将它保存的数值“取出来”。

事实上&#xff0c;Boost库已经提供了这样的类boost::any&#xff0c;下面我就为你讲述它的原理及构造。

首先&#xff0c;any类里面一定要提供一个模板构造函数和模板operator&#61;操作符。因为你必须允许用户写出&#xff1a;

any any_value(val);
any_value&#61;val1;

这样的代码。

其次&#xff0c;数据的存放之所是个问题&#xff0c;显然你不能将它保存在any类中&#xff0c;那会导致any类成为模板类&#xff0c;后者是明确不被允许的。数据应该动态存放&#xff0c;即动态分配一个数据的容器来存放数据&#xff0c;而any类中则保存指向这个容器的指针&#xff0c;明确地说&#xff0c;是指向这个容器的基类的指针&#xff0c;这是因为容器本身必须为模板&#xff0c;而any类中的指针成员又必须不是泛型的(因为any不能是泛型的&#xff0c;所以any中所有数据成员都不能是泛型的)&#xff0c;所以&#xff0c;结论是&#xff1a;为容器准备一个非泛型的基类&#xff0c;而让指针指向该基类。

下面就看一看boost库是如何具体实现这两点的。

any
{
:placeholder
{
:
virtual ~placeholder()
{}
:
virtual std::type_info & type() &#61; 0;
virtual placeholder * clone() &#61; 0;
};
holder : placeholder
{
:
holder( ValueType & value)
: held(value)
{}
:
virtual std::type_info & type()
{(ValueType); }
virtual placeholder * clone()
{holder(held);
}
:
ValueType held;
};
placeholder * content;

any( ValueType & value)
: content( holder(value)) {}
...

any & &#61;( ValueType & rhs)
{
any(rhs).swap(*); *;
}
any & swap(any & rhs)
{
std::swap(content, rhs.content); *;
}
~any()
{content; }
...
};

这虽然并非any的全部源代码&#xff0c;但是所有重要的思想已经表露无遗。剩下的部分只是一些简单的细节&#xff0c;请参见boost库的原文件。

“但是等等&#xff01;”&#xff0c;你急切的说&#xff1a;“你失去了型别的信息。”唔...的确&#xff0c;当赋值的模板函数返回后你也就失去了关于型别的信息。考虑下面你可能想要写出的代码&#xff1a;

i&#61;10;
boost::any anyVal&#61;i;j&#61;anyVal;

当转换操作符的设想彻底失败后&#xff0c;我们只能借助于某些“外来”的显式转换操作。就向static_cast<>一样。Boost提供了any_cast<>&#xff0c;于是你可以这样写&#xff1a;

j&#61;any_cast<>(anyVal);

事实上&#xff0c;any_cast的代码是这样的&#xff1a;


ValueType any_cast( any & operand)
{ValueType * result &#61; any_cast(&operand);(!result) bad_any_cast(); *result;
}

而any_cast针对指针的版本是这样&#xff1a;


ValueType * any_cast(any * operand)
{operand && operand->type() &#61;&#61; (ValueType) 1 ? & *>(operand->content)->held:0; }

这两个any_cast版本应该很好理解。后版本中的型别检查是必要的&#xff0c;如果没有这个检查&#xff0c;考虑以下代码&#xff1a;

i&#61;10;
boost::any anyVal&#61;i;d&#61;any_cast<>(anyVal);

这将通过编译&#xff0c;且运行期通常竟然也不会出错&#xff0c;下面我为你解释为什么会这样。

boost::anyVal&#61;i;其实将anyVal.content指针指向了一个holder对象(请回顾上面的代码)。然后any_cast(anyVal)实际上调用了any_cast<>针对指针的重载版本&#xff0c;并将anyVal的地址传递过去&#xff0c;也就是转到1处&#xff0c;因为调用的是any_cast&#xff0c;所以1处的代码被编译器特化为

2 *>(operand->content)->held

但是前面说过&#xff0c;operand->content实际指向的是any::holder&#xff0c;所以这个static_cast<>是“非法”的&#xff0c;然而事实是&#xff1a;它能通过编译&#xff01;原因很简单&#xff0c;holder和holder都是placeholder的基类。将基类指针向派生类指针转换被认为是合法的。但这却酿成大错&#xff0c;因为表达式2的型别将因此被推导为double&#xff01;原先holder只给int held;成员分配了sizeof(int)个字节的内存&#xff0c;而现在却要将int型的held当作double型来使用&#xff0c;也就是说使用sizeof(double)个字节内存。所以这就相当于&#xff1a;

i&#61;10;
* pd&#61;(*)(*)&i;d&#61;*pd;

使用typeinfo让我们有可能在运行时发现这种型别不符并及时抛出异常。但有个违反直观的事情是上面的那行错误的代码仍能通过编译&#xff0c;并且你也无法阻止它通过编译&#xff0c;因为holder和holder都是placeholder的基类。所以只能期望程序员们清楚自己在做什么&#xff0c;要不然就给他个异常瞧瞧。

使用boost::any实现virtual template成员函数

如你所知&#xff0c;C&#43;&#43;中没有提供virtual template function。然而有时候你的确会有这种需要,any可以一定程度上满足这种需要&#xff0c;例如&#xff0c;

Base
{
:
virtual Accept(boost::any anyData)
{
...
}
};Derived: Base
{:
virtual Accept(boost::any anyData)
{
...
}
};

这样的Accept函数能够接受任意类型的数据&#xff0c;并且是virtual函数

posted on 2009-01-09 18:46 GXW 阅读(...) 评论(...) 编辑 收藏

转:https://www.cnblogs.com/Fancyboy2004/archive/2009/01/09/1372954.html



推荐阅读
  • PHP反射API的功能和用途详解
    本文详细介绍了PHP反射API的功能和用途,包括动态获取信息和调用对象方法的功能,以及自动加载插件、生成文档、扩充PHP语言等用途。通过反射API,可以获取类的元数据,创建类的实例,调用方法,传递参数,动态调用类的静态方法等。PHP反射API是一种内建的OOP技术扩展,通过使用Reflection、ReflectionClass和ReflectionMethod等类,可以帮助我们分析其他类、接口、方法、属性和扩展。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了一些Java开发项目管理工具及其配置教程,包括团队协同工具worktil,版本管理工具GitLab,自动化构建工具Jenkins,项目管理工具Maven和Maven私服Nexus,以及Mybatis的安装和代码自动生成工具。提供了相关链接供读者参考。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文总结了在开发中使用gulp时的一些技巧,包括如何使用gulp.dest自动创建目录、如何使用gulp.src复制具名路径的文件以及保留文件夹路径的方法等。同时介绍了使用base选项和通配符来保留文件夹路径的技巧,并提到了解决带文件夹的复制问题的方法,即使用gulp-flatten插件。 ... [详细]
  • IT方面的论坛太多了,有综合,有专业,有行业,在各个论坛里混了几年,体会颇深,以前是论坛哪里人多 ... [详细]
  • CEPH LIO iSCSI Gateway及其使用参考文档
    本文介绍了CEPH LIO iSCSI Gateway以及使用该网关的参考文档,包括Ceph Block Device、CEPH ISCSI GATEWAY、USING AN ISCSI GATEWAY等。同时提供了多个参考链接,详细介绍了CEPH LIO iSCSI Gateway的配置和使用方法。 ... [详细]
  • 本文讲述了孙悟空写给白骨精的信件引发的思考和反省。孙悟空在信中对自己的行为进行了反思,认识到自己胡闹的行为并没有给他带来实际的收获。他也揭示了西天取经的真相,认为这是玉皇、菩萨设下的一场陷阱。他还提到了师傅的虚伪和对自己的实心话,以及自己作为师傅准备提拔的对象而被派下来锻炼的经历。他认为路上的九九八十一难也都是菩萨算计好的,唐僧并没有真正的危险。最后,他提到了观音菩萨在关键时刻的指导。这封信件引发了孙悟空对自己行为的思考和反省,对西天取经的目的和自己的角色有了更深入的认识。 ... [详细]
  • 本文介绍了在Ubuntu下制作deb安装包及离线安装包的方法,通过备份/var/cache/apt/archives文件夹中的安装包,并建立包列表及依赖信息文件,添加本地源,更新源列表,可以在没有网络的情况下更新系统。同时提供了命令示例和资源下载链接。 ... [详细]
  • 本文介绍了Cocos2dx学习笔记中的更新函数scheduleUpdate、进度计时器CCProgressTo和滚动视图CCScrollView的用法。详细介绍了scheduleUpdate函数的作用和使用方法,以及schedule函数的区别。同时,还提供了相关的代码示例。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
author-avatar
无声胜有剩
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有