仿boost::any的泛型指针类any的实现
在boost库中,any是一种特殊容器,只能容纳一个元素,但这个元素可以是任意的类型----int、double、string、标准容器或者任何自定义类型。程序可以用any保存任意的数据,也可以在任何需要的时候取出any中的数据。any类目前已经加入到c++17标准中,在vs2017中include<any>头文件即可使用。
vs2017里的标准库any的头文件最后有句提示:
#pragma message("class any is only available with C++17 or later.") 也就是说此any类无法在支持C++17标准之前的编译器中使用。
仿boost的any实现如下,代码加了注释,方便读者学习理解
//any类设计要点
//1.any类不能是一个模板类形如int i;any<int>anyValue=i;无意义,还不如直接写int anyValue =i;
//2.any必须提供模板构造函数(赋值操作不必是模板的),才能完成如下操作:
// int i; long j; struct X; X x;any anyValue(i); anyValue=j; anyValue =x;
//3.必须提供某些有关它所保存的对象型别的信息。
//4.它必须能保存数值且提供某种方法将它保存的数值“取出来”。
//5.数据不能放在any类里,这会使any类成为模板类,不符合1的要求。
// 数据应该动态存放,即动态分配一个数据的容器来存放数据,而any类中则保存指向这个容器的指针。
// 明确地说,是指向这个容器的基类的指针,这是因为容器本身必须为模板,而any类中的指针成员又必须不是泛型的。
// 因为any不能是泛型的,所以any中所有数据成员都不能是泛型的。
// 所以,结论是:为容器准备一个非泛型的基类,而让指针指向该基类。
#pragma once
#include <memory>
#include <typeindex>
#include <exception>
#include <iostream>
struct Any
{
Any(void) : m_tpIndex(std::type_index(typeid(void))) {
std::cout << "Any(void) function called!" << std::endl; }
Any(const Any& that) : m_ptr(that.Clone()), m_tpIndex(that.m_tpIndex) {
std::cout << "Any(const Any& that) called!" << std::endl; }
Any(Any && that) : m_ptr(std::move(that.m_ptr)), m_tpIndex(that.m_tpIndex) {
std::cout << " Any(Any && that) function called!" << std::endl; }
//创建智能指针时,对于一般的类型,通过std::decay来移除引用和cv符,从而获取原始类型
//cv符即const和violate
//类似int i;long j;anyValue(i),anyValue(j)时调用模板构造函数
//注意构造函数中的new Derived。由于Derived是模板类,U里包含的类型信息可以完整的被保留。
//构造函数本身又可以传进任意类型,这样就实现了类型类型擦除
template<typename U, class = typename std::enable_if<!std::is_same<typename std::decay<U>::type, Any>::value, U>::type>
Any(U && value) : m_ptr(new Derived < typename std::decay<U>::type>(std::forward<U>(value))),\
m_tpIndex(std::type_index(typeid(typename std::decay<U>::type))){
std::cout << "Any(U && value) template function called!" << std::endl;}
template<class U> bool Is() const
{
std::cout << "Any::Is() template function called!" << std::endl;
return m_tpIndex == std::type_index(typeid(U));
}
bool IsNull() const {
std::cout << "Any::IsNull() function called!" << std::endl;
return !bool(m_ptr);
}
//AnyCast方法的思想是期望程序员们清楚自己在做什么,要不然就给他个异常瞧瞧。 //AnyCast模仿了boost提供的any_cast<>模板方法,在这里是Any的成员函数
//取出原始类型,需要客户提供类型信息 如 anyValue.AnyCast<int>
//m_tpIndex里保存了关于原始数据的型别信息,不存在boost::any实现时丢失型别信息的情况
//所以boost单独提供了any_cast版本出来,而这里不需要
template<class U>
U& AnyCast()
{
std::cout << "Any::AnyCast() template function called!" << std::endl;
//判断类型信息是否匹配,不匹配报错
if (!Is<U>())
{
std::cout << "can not cast " << typeid(U).name() << " to " << m_tpIndex.name() << std::endl;
throw std::logic_error{ "bad cast" };
}
//匹配了,则取出原始数据
auto derived = dynamic_cast<Derived<U>*> (m_ptr.get());
return derived->m_value;
}
//赋值操作重载不是模板函数,不存在模板无穷递归即循环赋值问题
Any& operator=(const Any& a)
{
std::cout << "Any& operator= function called!" << std::endl;
//防止自赋值
if (m_ptr == a.m_ptr)
return *this;
//两个数据成员赋值
m_ptr = a.Clone();
m_tpIndex = a.m_tpIndex;
return *this;
}
private:
struct Base;
typedef std::unique_ptr<Base> BasePtr;
//泛型数据容器Derived的非泛型基类
struct Base
{
virtual ~Base() {
std::cout << " virtual ~Base() function called!" << std::endl;
} //虚析构函数,为保证派生类对象能用基类指针析构
virtual BasePtr Clone() const = ; //复制容器
}; //Derived是个模板类,各个类成员之间可以共享类模板参数的信息
//所以,可以方便地用原数据类型来进行各种操作
template<typename T>
struct Derived : Base
{
template<typename U>
Derived(U && value) : m_value(std::forward<U>(value)) {
std::cout << "Derived(U && value) function called!" << std::endl;
}
BasePtr Clone() const
{
std::cout << "Derived::Clone() function called!" << std::endl;
return BasePtr(new Derived<T>(m_value)); //改写虚函数,返回自身的复制体,动态构造
}
T m_value; //原始数据保存的地方
};
//any类转发Clone操作给BasePtr的Clone方法,实现数据复制
BasePtr Clone() const
{
std::cout << "Any::Clone() function called!" << std::endl;
if (m_ptr != nullptr)
return m_ptr->Clone();
return nullptr;
}
//通过基类指针擦除具体类型
//any类赋值时需要创建派生类对象,这里用std::unique_ptr管理派生类对象生命周期
BasePtr m_ptr;
std::type_index m_tpIndex; //提供关于型别的信息
};
测试代码如下:
// testany.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "any.hpp"
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
Any n;
std::cout << n.IsNull() << std::endl;
std::string str = "Hello";
//赋值操作,其实分两步进行
//先调用any的移动构造函数把str转换为any类型
//再调用any的赋值构造函数完成真正的赋值
n = str;
try
{
std::cout << n.AnyCast<int>() << '\n';
}
catch (std::exception& e)
{
std::cout << "Exception: " << e.what() << '\n';
}
std::cout << std::endl;
std::cout << "-----------------------------" << std::endl;
//直接初始化只调用移动构造函数,比采用赋值构造少了很多步骤,所以直接初始化效率更高
Any n1(1);
std::cout << n1.Is<int>() << '\n';
std::cout << std::endl;
std::cout << "-----------------------------" << std::endl;
Any n2(std::move(n1));
std::cout << n2.AnyCast<int>() << '\n';
std::cout << std::endl;
system("pause");
return ;
}
在vs2013上测试结果如下:
参考:
C++中的类型擦除(type erasure in c++)
仿boost::any的泛型指针类any的实现的更多相关文章
- Boost源码剖析之:泛型指针类any
C++是强类型语言,所有强类型语言对型别的要求都是苛刻的,型别一有不合编译器就会抱怨说不能将某某型别转换为某某型别,当然如果在型别之间提供了转换操作符或是标准所允许的一定程度的隐式转换(如经过非exp ...
- (七)boost库之单例类
(七)boost库之单例类 一.boost.serialzation的单件实现 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一 ...
- Boost中的智能指针(转)
这篇文章主要介绍 boost中的智能指针的使用.(转自:http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html) 内存管理是一 ...
- sp<> 强指针类的用法
在android 中可以广泛看到的template<typename T>, class Sp 句柄类实际上是android 为实现垃圾回收机制的智能指针.智能指针是c++ 中的一个概念 ...
- RestTemplate将响应数据转换为具有泛型的类对象
前言: 重要,RestTemplate在SpringBoot项目里即便通过HttpMessageConverters添加了Fastjson且优先级比jackson要高也不会在RestTemplate里 ...
- C++之不带指针类的设计——Boolean
经典的类设计分类 带指针类 不带指针类 Header文件的布局 #ifndef __COMPLEX__ #define __COMPLEX__ #include <iostream.h> ...
- 智能指针类模板(上)——STL中的智能指针
智能指针类模板智能指针本质上就是一个对象,它可以像原生指针那样来使用. 智能指针的意义-现代C++开发库中最重要的类模板之一-C++中自动内存管理的主要手段-能够在很大程度上避开内存相关的问题 1.内 ...
- C++中的智能指针类模板
1,智能指针本质上是一个对象,这个对象可以像原生的指针一样使用,因为智能指 针相关的类通过重载的技术将指针相关的操作符都进行了重载,所以智能指针对象可以像原生指针一样操作,今天学习智能指针类模板,通过 ...
- Iterator泛型指针
Iterator泛型指针 每个标准容器都提供一个名为: begin()的操作函数,返回一个iterator指向第一个元素: end()操作函数,返回一个iterator指向最后一个元素的下一位置: 定 ...
随机推荐
- 国内+海外IDC资源合作
主营业务:服务器租用.托管.机柜大带宽.安全防御.云主机.海外专线.海外托管.CDN加速.站群 资源覆盖: 华南:广东东莞.深圳.广州.湛江.福建厦门.泉州.福州 华北:北京.天津.山东 华东:江苏苏 ...
- spring aop 学习1
1.配置自动扫描的包 <context:component-scan base-package="com.ddf.spring.aop.impl"/> 2.使用spri ...
- leetcode20
public class Solution { Stack<char> S = new Stack<char>(); public bool IsValid(string s) ...
- 003之MFCSocket异步编程(指针机制)
002篇是采用传统方式创建,不适应动态的网络环境,服务器为客户端保留着断开连接时的套接字,不够灵活.而采用指针机制不仅可以更加灵活性,而且能使代码更集中,更具有条理性.将其转变成指针机制.功能及运行保 ...
- win7频繁提示资源管理器已停止工作解决办法
方法一,重新启动资源管理器,右键点击桌面下方的“任务栏”空白区,在弹出的菜单栏中选择“任务管理器”. 进入任务管理器,点击上方的“文件”,选择新建任务. 在弹出的对话框中,输入explorer ...
- Django_models下划线__正反查询,对象正反查询
1.我们使用models对数据库进行查询的时候,想去拿到结果的时候有时直接使用".字段",有时是'[0].字段',区别就是我们使用的语句返回的到底是一个对象还是列表: obj=mo ...
- mysql命令行常用指令
一. 启动mysql:service mysql start 停止mysql:service mysql stop 重启mysql:service mysql restart 查看mysql服务状态: ...
- [leetcode]77. Combinations组合
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. Example: I ...
- 利用maven将项目打包成一个可以运行的独立jar包
目标:希望把Java项目打包成一个完整的jar包,可以独立运行,不需要再依赖其他jar包. 我们在用eclipse中mvn创建mvn项目的时候,选择非webapp,会默认的以jar打包形式,如下图: ...
- Scanner 随机数
import java.util.Scanner; import java.util.Scanner; Sc ...