c++11 boost技术交流群:296561497,欢迎大家来交流技术。

关于类型擦除,可能很多人都不清楚,不知道类型擦除是干啥的,为什么需要类型擦除。有必要做个说明,类型擦除就是将原有类型消除或者隐藏。为什么要擦除类型?因为很多时候我不关心具体类型是什么或者根本就不需要这个类型,通过类型擦除我们可以获取很多好处,比如使得我们的程序有更好的扩展性、还能消除耦合以及消除一些重复行为,使程序更加简洁高效。归纳一下c++中类型擦除方式主要有如下五种:

第一种:通过多态来擦除类型

第二种:通过模板来擦除类型

第三种:通过某种容器来擦除类型

第四种:通过某种通用类型来擦除类型

第五种:通过闭包来擦除类型

第一种类型隐藏的方式最简单也是我们经常用的,通过将派生类型隐式转换成基类型,再通过基类去多态的调用行为,在这种情况下,我不用关心派生类的具体类型,我只需要以一种统一的方式去做不同的事情,所以就把派生类型转成基类型隐藏起来,这样不仅仅可以多态调用还使我们的程序具有良好的可扩展性。然而这种方式的类型擦除仅仅是部分的类型擦除,因为基类型仍然存在,而且这种类型擦除的方式还必须是继承方式的才可以,而且继承使得两个对象强烈的耦合在一起了,正是因为这些缺点,通过多态来擦除类型的方式有较多局限性效果也不好。这时我们通过第二种方式擦除类型,以解决第一种方式的一些缺点。通过模板来擦除类型,本质上是把不同类型的共同行为进行了抽象,这时不同类型彼此之间不需要通过继承这种强耦合的方式去获得共同的行为了,仅仅是通过模板就能获取共同行为,降低了不同类型之间的耦合,是一种很好的类型擦除方式。然而,第二种方式虽然降低了对象间的耦合,但是还有一个问题没解决,就是基本类型始终需要指定,并没有消除基本类型,例如,我不可能把一个T本身作为容器元素,必须在容器初始化时就要知名这个T是具体某个类型。这时多么希望有一种通用的类型啊,可以让我的容器容纳所有的类型,就像c#和java中的object类型一样,是所有类型的基类。c++中没有这种object类型怎么办?也许有人想到了,可以用boost.variant类型,是的,boost.variant可以把各种不同的类型包起来,从而让我们获得了一种统一的类型,而且不同类型的对象间没有耦合关系,它仅仅是一个类型的容器。让我们看看怎么用boost.variant来擦除类型。

struct blob
{const char *pBuf;int size;
};
//定义通用的类型,这个类型可能容纳多种类型
typedef boost::variant<double, int, uint32_t, sqlite3_int64, char*, blob, NullType>Value;vector<Value> vt; //通用类型的容器,这个容器现在就可以容纳上面的那些类型的对象了
vt.push_back(1);
vt.push_back("test");
vt.push_back(1.22);
vt.push_back({"test", 4}); 

View Code

上面的代码就擦除了不同类型,使得不同的类型都可以放到一个容器中了,如果要取出来就很简单,通过get<T>(Value)就可以获取对应类型的值了。这种方式是通过某种容器把类型包起来了,从而达到类型擦除的目的。它的缺点是这个通用的类型必须事先定义好,它只能容纳声明的那些类型,增加一种新类型就不行了。通过第四种方式可以消除这个缺点,通过某种通用类型来擦除类型。类似于c#和java中的object类型。这种通用类型是通过boost.any实现的,它不需要预先定义类型,不同类型都可以转成any。让我们看看怎么用any来擦除类型的。

unordered_map<string, boost::any> m_creatorMap;
m_creatorMap.insert(make_pair(strKey, new T)); //T may be any type
boost::any obj = m_creatorMap[strKey];
T t = boost::any_cast<T>(obj);

需要注意的是,第四和第五种方式虽然解决了第三种方式不能彻底消除基本类型的缺点,但是还存一个缺点,就是取值的时候仍然依赖于具体类型,无论我是通过get<T>还是any_case<T>,我都要T的具体类型,这在某种情况下仍然有局限性。例如,有这样一种场景:
我有A、B、C、D四种结构体,每个结构体中有某种类型的指针,名称且称为info,我现在提供了返回这些结构体的四个接口供外接使用,有可能是c#或者dephi调用这些接口,由于结构体中的info指针是我分配的内存,所以我必须提供释放这些指针的接口。代码如下:

struct A
{int* info;int id;
};struct B
{double* info;int id;
};struct C
{char* info;int id;
};struct D
{float* info;int id;
};//对外提供的删除接口
void DeleteA(A& t)
{delete t.info;
}void DeleteB(B& t)
{delete t.info;
}void DeleteC(C& t)
{delete t.info;
}void DeleteD(D& t)
{delete t.info;
}

大家可以看到,增加的四个删除函数内部都是重复代码,本来通过模板函数一行搞定,但是没办法,c#可没有c++的模板,还得老老实实的提供这些重复行为的接口,而且这种方式还有个坏处就是每增加一种类型就得增加一个重复的删除接口,怎么办?能统一成一个删除接口吗?可以,一个可行的办法就是将分配的内存通过一个ID关联并保存起来,让外接传一个ID,告诉我要删那块内存,新的统一删除函数可能是这样:

//内部将分配的内存存到map中,让外面传ID,内部通过ID去删除对应的内存块
map<int, T> mapT;template<typename R, typename T>
R GetT()
{R result{1,new T()};mapT.insert(std::pair<int, T>(1, R)); return result;
}//通过ID去关联我分配的内存块,外面传ID,内部通过ID去删除关联的内存块
void DeleteT(const int& id)
{R t = mapT[id]->second();delete t.info;
}

很遗憾,上面的代码编译不过,因为,map<int, T> mapT只能保存一种类型的对象,无法把分配的不同类型的对象保存起来,我们可以通过方式三和方式四,用variant或者any去擦除类型,解决T不能代表多种类型的问题,第一个问题解决。但是还有第二个问题,DeleteT时,从map中返回的variant或者any,无法取出来,因为接口函数中没有类型信息,而取值方法get<T>和any_cast<T>都需要一个具体类型。似乎进入了死胡同,无法只提供一个删除接口了。但是办法总还是有的。
方式五隆重登场了,看似无解的问题,通过方式五就能解决了。通过闭包来擦除类型很好很强大。在介绍方式五之前,我要先介绍一下闭包,闭包也可以称为匿名函数或者lamda表达式,c++11中的lamda表达式就是c++中的闭包,c++11引入lamda,实际上引入了函数式编程的概念,函数式编程有很多优点,使代码更简洁,而且声明式的编码方式更贴近人的思维方式。函数式编程在更高的层次上对不同类型的公共行为进行了抽象,从而使我们不必去关心具体类型。关于函数式编程的优点就不多说了。下面看看如何使用方式五去解决上面的问题。

std::map < int, std::function <void()>> m_freeMap; //保存返回出去的内存块

template<typename R, typename T>
R GetResult()
{R result = GetTable<R, T>();    m_freeMap.insert(std::make_pair(result.sequenceId, [this, result]{FreeResult(result);        }));
}bool FreeResultById(int& memId){auto it = m_freeMap.find(memId);if (it == m_freeMap.end())return false;it->second(); //delete by lamda
        m_freeMap.erase(memId);return true;}    

总结:通过闭包去擦除类型,可以解决前面四种擦除方式遇到的问题,优雅而简单!

转载于:https://www.cnblogs.com/qicosmos/p/3256022.html

(原创)c++中的类型擦除相关推荐

  1. java中的类型擦除type erasure

    文章目录 简介 举个例子 原因 解决办法 总结 java中的类型擦除type erasure 简介 泛型是java从JDK 5开始引入的新特性,泛型的引入可以让我们在代码编译的时候就强制检查传入的类型 ...

  2. 泛型中的类型擦除和桥方法

    在Java中,泛型的引入是为了在编译时提供强类型检查和支持泛型编程.为了实现泛型,Java编译器应用类型擦除实现: 1.  用类型参数(type parameters)的限定(如果没有就用Object ...

  3. java 类型擦除_java中的类型擦除type erasure

    简介 泛型是java从JDK 5开始引入的新特性,泛型的引入可以让我们在代码编译的时候就强制检查传入的类型,从而提升了程序的健壮度. 泛型可以用在类和接口上,在集合类中非常常见.本文将会讲解泛型导致的 ...

  4. php的strpos不支持数字,php使用strpos判断字符串中数字类型子字符串出错的解决方法 原创...

    php使用strpos判断字符串中数字类型子字符串出错的解决方法 原创 这里有新鲜出炉的 PHP 面向对象编程,程序狗速度看过来! PHP 开源脚本语言 PHP(外文名: Hypertext Prep ...

  5. php判断是字符串类型,php使用strpos判断字符串中数字类型子字符串出错的解决方法 原创...

    本文实例讲述了php使用strpos判断字符串中数字类型子字符串出错的解决方法.分享给大家供大家参考,具体如下: 一.问题: 最近的开发中在程序代码里有一个随机数是否在给定字符串里的判断,我用了如下的 ...

  6. java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题

    原 java泛型(二).泛型的内部原理:类型擦除以及类型擦除带来的问题 2012年08月29日 23:44:10 Kilnn 阅读数:56717 版权声明:本文为博主原创文章,未经博主允许不得转载. ...

  7. java什么是类型擦除_Java 泛型,你了解类型擦除吗?

    泛型,一个孤独的守门者. 大家可能会有疑问,我为什么叫做泛型是一个守门者.这其实是我个人的看法而已,我的意思是说泛型没有其看起来那么深不可测,它并不神秘与神奇.泛型是 Java 中一个很小巧的概念,但 ...

  8. 【C++】 66_C++ 中的类型识别

    类型识别 在面向对象中可能出现下面的情况 基类指针指向子类对象(赋值兼容性) 基类引用成为子类对象的别名(赋值兼容性) 静态类型 - 变量(对象)自身的类型[编译期可确定] 动态类型 - 指针(引用) ...

  9. java泛型的泛型_Java 泛型总结(一):基本用法与类型擦除

    简介 Java 在 1.5 引入了泛型机制,泛型本质是参数化类型,也就是说变量的类型是一个参数,在使用时再指定为具体类型.泛型可以用于类.接口.方法,通过使用泛型可以使代码更简单.安全.然而 Java ...

最新文章

  1. ICRA 2022 | 基于多模态变分自编码器的任意时刻三维物体重建
  2. 平时用电脑的一些技巧
  3. 交叉编译并移植opencv3.0.0到arm板
  4. 服务器压力测试_魔兽世界怀旧服:美服18日开放安其拉开门测试,P5阶段即将来临...
  5. 计算机一级考试教学设计,《全国计算机一级考试》教学设计说明.doc
  6. 用javascript写Android和iOS naitve应用,实在炫酷。
  7. Nessus虚拟机的几个问题解决办法
  8. 音乐标签编辑器 Meta 2.0.0 for Mac
  9. 计算机中submit commit区别
  10. 计算机网络 IP:ABC类地址与 子网掩码(ABC类网络均有属于自己的私有IP段 节省32位IP资源 ) 网络地址 主机地址 子网 与子网掩码之间的关系换算 IP和子网掩码相互依存
  11. PHP 快速解决跨域问题
  12. Ubuntu操作系统的学习,从新手到老手的过渡
  13. C++进程和Python进程通信
  14. 线性表的链式存储结构 ( 链表 )
  15. 产品读书《终结拖延症》
  16. 前端智能化实践——可微编程
  17. 读书笔记《推荐系统实战》| 好的推荐系统
  18. 2023年四川农业大学农村发展专硕经验贴
  19. 图形学 Lecture7 光线追踪
  20. [读论文]CVPR2019: Occupancy Networks: Learning 3D Reconstruction in Function Space

热门文章

  1. 阿里面试 Java 都问什么?万字总结!
  2. StringBuilder 为什么线程不安全?
  3. 我的HTTP/3学习笔记
  4. Java管理Cookie增删改查操作
  5. 我是如何从一个新闻狗转行成为程序猿的?
  6. Linux基础——linux和gcc的介绍
  7. Java集合:Integer.parseInt() String.substring()
  8. 11.C++: cin、cin.get()、cin.getline()、getline()、gets()
  9. 引入spring-boot-starter-actuator,控制台没有mapper的映射信息打印问题
  10. JPA-CascadeType四个属性的讲解