泛型编程杂谈

2023-10-30

泛型 (GP)之前, 先谈一下面向对象(OO), OO强调世界是由对象组成的,对象是由方法和属性组成的(个人感觉还应该加上事件),而对象之间又有继承(is-a)和组合等 关系。OO很符合我们认识世界的直觉,它以封装,继承和多态为特性,我们在现实工作中又总结出来了OO的5大设计原则和23种设计模式。总之,OO基本上已经可以很好的解决我们现实生活中的所有问题。

那么既然OO已经可以很好的解决我们的问题了,为什么还要有GP? 

我们先来看一下OO的缺点:
OO的多态是通过继承自同一接口来实现的, 修改接口会导致所有派生类的修改,耦合比较紧密。
OO的多态是运行时的, 性能比较低。
OO的多态通过抽象接口实现,使用不安全,在不支持反射的语言(比如C++)中会丧失类型检测。 

用OO设计时,你只能通过抽象接口来处理不同的事物。比如你要让Cat和Dog同时跑动,你可能会抽象出一个IAnimal接口,内部有一个Run的方法,然后让Cat和Dog分别继承。但是如果你想让猫(Cat)和玩具猫(ToyCat)支持跑动,这时你抽象出IAnimal就不合适了, 你可能会抽象出ICatProperty。可以看到OO的这种继承体系非常笨重,到后面可能会导致非常深的继承层次(比如MFC),代码扩展和维护都很艰难。

上面OO的这些缺点在GP没有被发现之前都不是缺点,因为我们没有更好的解决方案,但是人们逐渐发现了GP,GP最初是在使用容器(Container)时被发现的,container<T>可以放任意类型的元素,根据元素类型,我们可以可以生成任意类型的container. 这些技术后来发展成 泛型 编程和模板元编程。

那么什么是GP?
我的理解是 GP是基于concept的程序设计,我们通过concept来定义对象之间的关系。

什么是concept?
按我的理解,concept就是我们自己定义的概念和规则。

比如说任何对象,只要可赋值,他就符合Assignable的concept:
class  A {};
class  B {};

void  test()
{
   A a1; A a2; a1  =  a2;
   B b1; B b2; b1  =  b2;
}
上面的A和B就都符合Assignable的concept.

又比如任何对象,只要可比较, 他就符合Comparable的concept:
class  C
{
    public :
        bool  operator  <  ( const  C &  c);
        bool  operator  >  ( const  C &  c);
        bool  operator  ==  ( const  C &  c);
        bool  operator  !=  ( const  C &  c);
        bool  operator  <=  ( const  C &  c);
        bool  operator  >=  ( const  C &  c);
};
上面的对象C,他就符合他就符合Comparable的concept。

再看一个例子:
class  Dog 
{
public
    void  run();
};

class  Cat
{
public
    void  run();
};

template < typename T >
void  run(T &  t)
{
    t.run();
}
上面的Cat和Dog就都符合拥有成员函数Run这个concept.

GP的concept可以是任何我们能想到的规则,比如包含某个成员变量,或是定义某个类型, 比如下面代码:
class  myTraits
{
public :
   typedef  int  type;
   type value;
};

template < typename T >
typename T::type GetValue(T &  t)
{
    return  t.value;
}

甚至GP的concept可以是指明基类和派生类, 比如下面代码:

template < typename TDrived, typename TBase >
class  CMyImpl:  public  TBase
{
public :
      void  work()
   { 
      TDrived *  p  =  static_cast < TDrived *> ( this );
      p -> Hello();
     }
};

class  CMyBase {};
class  CMyClass:  public  CMyImpl < CMyClass, CMyBase >
{
public :
      void  Hello() {}
};
ATL中大量应用这种concept来实现代码重用和模拟虚函数。

这些concept在不同语言中定义不一样,在一些语言中(比如C#)我们可以通过定义来进行显式约束,表明我们某个concept要满足哪些条件;在有些语言中(比如C++)则没有这显式约束定义,我们只能通过我们编程时自己的逻辑来保证,当然如果某个方法要求他的对象满足某个concept而你的对象没有满足 ,编译器也是不会让你通过的。

另外GP的concept之间本身也有某种关系, 某个concept可能继承与另外一个concept,或者说某个concept是另外一个concept的强化,也就是满足某个concept的对象肯定也同时满足另外一个concept. 比如concept  A包含Run方法, concept B包含Run和Eat方法,则concept B是concept A的强化,满足concept B的对象肯定同时满足concept A。

我们可以看到GP的这种基于concept的设计方式,大大降低了对象之间的耦合性,我们不再要求象OO那样抽象出共同的接口来让大家继承;任何两个看似没有关系的对象, 只要他们满足某个concept的约束,他们就能当作模板参数传给GP代码(模板类或是模板函数)。另外,我们可以看到GP的这些类型检测是在编译时就完成的,他的多态是在编译时就确定的静多态, 效率大大高于OO的动多态。
 
有些人说GP的抽象能力高于OO,这个观点我并不认同,我感觉只是他们的抽象方式不一样,OO是基于接口, 而GP是基于concept。OO的基于接口的抽象,在源代码和最终运行时都能体现,源代码中是接口,运行时是虚表,所以他们是一致的, 符合普通人的思维习惯。GP基于concept的抽象, 主要体现在源代码中 ,只是你用来告诉编译器你的思维方式, 在运行时他可能是一个完全不同的世界,所以比较难理解。

如果说OO的设计是抽象出接口, GP的设计就是抽象出concept, 满足某个concept的class是一个template class(如template<typename T> class vector), 而template class的又可以实例化成某个特定的class(如vector<int>)。 所以GP可以大大减少我们源代码的数量,但是他本身不能减小我们最终编译的可执行文件的大小,相反如果模板类过大,反而会造成代码膨胀,而OO的继承则没有这个问题(可参考 C++模板会使代码膨胀吗 )。

对于在实际开发中主要用OO还是用GP的问题, 可能应人和公司而异,最好的方式当然是OO和GP的灵活结合。有人觉得GP的代码不好理解,那是因为你没有理解他的concept,比如看了《 泛型 编程与STL》,你就会发现自己也可以扩展STL了。当然,因为C++对concept没有约束定义机制, 而concept这个东西注释又不好描述,所以大部分时候我们只能通过源代码来推理,这在一定程度上也造成了GP代码难读懂这种说法的流行。

不知道大家在实际开发中GP用的多不多?

转载于:https://www.cnblogs.com/weiym/archive/2012/11/10/2764013.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

泛型编程杂谈 的相关文章

  • 在 LINQ 查询中返回不带时间的日期

    我正在编写一个查询 我想计算按日期联系我们的呼叫中心的次数 看起来很简单 但由于联系日期字段是日期时间字段 我得到了时间 因此当我按联系日期 时间 分组时 每个联系日期实例的计数为 1 所以 我想只按日期分组 而不按时间分组 下面是我用来查
  • 属性对象什么时候创建?

    由于属性实际上只是附加到程序集的元数据 这是否意味着属性对象仅根据请求创建 例如当您调用 GetCustomAttributes 时 或者它们是在创建对象时创建的 或者 前两个的组合 在由于 CLR 的属性扫描而创建对象时创建 从 CLR
  • 如何在 Unity 中从 RenderTexture 访问原始数据

    问题的简短版本 我正在尝试访问 Unity 中 RenderTexture 的内容 我一直在使用 Graphics Blit 使用自己的材质进行绘制 Graphics Blit null renderTexture material 我的材
  • Func 方法参数的首选命名约定是什么?

    我承认这个问题是主观的 但我对社区的观点感兴趣 我有一个缓存类 它采用类型的缓存加载器函数Func
  • 嵌入式系统中的malloc [重复]

    这个问题在这里已经有答案了 我正在使用嵌入式系统 该应用程序在 AT91SAMxxxx 和 cortex m3 lpc17xxx 上运行 我正在研究动态内存分配 因为它会极大地改变应用程序的外观 并给我更多的力量 我认为我唯一真正的路线是为
  • 为什么 POSIX 允许在只读模式下超出现有文件结尾 (fseek) 进行搜索

    为什么寻找文件结尾很有用 为什么 POSIX 让我们像示例中那样在以只读方式打开的文件中进行查找 c http en cppreference com w c io fseek http en cppreference com w c io
  • c 中的错误:声明隐藏了全局范围内的变量

    当我尝试编译以下代码时 我收到此错误消息 错误 声明隐藏了全局范围内的变量 无效迭代器 节点 根 我不明白我到底在哪里隐藏或隐藏了之前声明的全局变量 我怎样才能解决这个问题 typedef node typedef struct node
  • HttpClient 像浏览器一样请求

    当我通过 HttpClient 类调用网站 www livescore com 时 我总是收到错误 500 可能服务器阻止了来自 HttpClient 的请求 1 还有其他方法可以从网页获取html吗 2 如何设置标题来获取html内容 当
  • 基于范围的 for 循环中的未命名循环变量?

    有没有什么方法可以不在基于范围的 for 循环中 使用 循环变量 同时也避免编译器发出有关未使用它的警告 对于上下文 我正在尝试执行以下操作 我启用了 将警告视为错误 并且我不想进行像通过在某处毫无意义地提及变量来强制 使用 变量这样的黑客
  • 使用向量的 merge_sort 在少于 9 个输入的情况下效果很好

    不知何故 我使用向量实现了合并排序 问题是 它可以在少于 9 个输入的情况下正常工作 但在有 9 个或更多输入的情况下 它会执行一些我不明白的操作 如下所示 Input 5 4 3 2 1 6 5 4 3 2 1 9 8 7 6 5 4 3
  • 线程、进程和 Application.Exit()

    我的应用程序由主消息循环 GUI 和线程 Task Factory 组成 在线程中我调用一些第三方应用程序var p new Process 但是当我调用Application Exit 在消息循环中 我可以看到在线程中启动的进程仍在内存中
  • 像“1$”这样的位置参数如何与 printf() 一起使用?

    By man I find printf d width num and printf 2 1 d width num 是等价的 但在我看来 第二种风格应该与以下相同 printf d num width 然而通过测试似乎man是对的 为什
  • AccessViolationException 未处理

    我正在尝试使用史蒂夫 桑德森的博客文章 http blog stevensanderson com 2010 01 28 editing a variable length list aspnet mvc 2 style 为了在我的 ASP
  • EPPlus Excel 更改单元格颜色

    我正在尝试将给定单元格的颜色设置为另一个单元格的颜色 该单元格已在模板中着色 但worksheet Cells row col Style Fill BackgroundColor似乎没有get财产 是否可以做到这一点 或者我是否必须在互联
  • 如何构建印度尼西亚电话号码正则表达式

    这些是一些印度尼西亚的电话号码 08xxxxxxxxx 至少包含 11 个字符长度 08xxxxxxxxxxx 始终以 08 开头 我发现这个很有用 Regex regex new Regex 08 0 9 0 9 0 9 0 9 0 9
  • 如何在内存中存储分子?

    我想将分子存储在内存中 这些可以是简单的分子 Methane CH4 C H bond length 108 7 pm H H angle 109 degrees But also more complex molecules like p
  • 在Linux中使用C/C++获取机器序列号和CPU ID

    在Linux系统中如何获取机器序列号和CPU ID 示例代码受到高度赞赏 Here http lxr linux no linux v2 6 39 arch x86 include asm processor h L173Linux 内核似
  • 如何在 C# 中播放在线资源中的 .mp3 文件?

    我的问题与此非常相似question https stackoverflow com questions 7556672 mp3 play from stream on c sharp 我有音乐网址 网址如http site com aud
  • 将 viewbag 从操作控制器传递到部分视图

    我有一个带有部分视图的 mvc 视图 控制器中有一个 ActionResult 方法 它将返回 PartialView 因此 我需要将 ViewBag 数据从 ActionResult 方法传递到 Partial View 这是我的控制器
  • 为什么 strtok 会导致分段错误?

    为什么下面的代码给出了Seg 最后一行有问题吗 char m ReadName printf nRead String s n m Writes OK char token token strtok m 如前所述 读取字符串打印没有问题 但

随机推荐

  • 前端面试题汇总

    前端面试题汇总 一 Html css js 1 Html5新增的语义化标签有哪些 语义化标签的优点 提升可访问性 SEO 结构清晰 利于维护 header页面头部 main页面主要内容 footer页面底部 Nav导航栏 aside侧边栏
  • pyqt5弹出提示框

    弹窗的分类 1 软件关闭提示弹框 2 信息提示弹框 3 错误提示弹框 4 信息警告弹框 5 关于弹窗 模块导入 from PyQt5 QtWidgets import QMessageBox 1 软件关闭弹框 这类的弹框一般是在整个软件关闭
  • Shell脚本入门 03:字符串与数组

    文章目录 目的 字符串 获取字符串长度 拼接字符串 截取字符串 替换字符串 判断与默认值 内嵌文档与内嵌字符串 内嵌文档 Here Document 内嵌字符串 Here String 数组 数组定义与使用 获取数组长度 拼接数组 删除数组
  • UBUNTU20.04配置qt+vtk+pcl+libtorch+cuda+cudnn+tensorrt开发环境

    总目录 1 环境及版本说明 2 下载并安装Qt 3 下载并编译vtk 4 下载并编译PCL依赖库 4 1 metslib 4 2 libpng 4 3 libpcap 5 编译PCL 6 cuda cudnn tensorrt安装与测试 6
  • 关于js导出excel的标题日期问题

    注意 获取日期放入标题和sheet中 不能用空格 否则不好使 var startTime startTime val substring 0 10 var endTime endTime val substring 0 10 var tit
  • STM32系统学习——TIM(基本定时器)

    一 定时器分类 STM32F1 系列中 除了互联型的产品 共有 8 个定时器 分为基本定时器 通用定时器和高级定时器 基本定时器 TIM6 和 TIM7 是一个 16 位的只能向上计数的定时器 只能定时 没有外部 IO 通用定时器 TIM2
  • qemu-linux-user ELF vs XCOFF 1

    最后更新2021 12 14 注 以下及以后本系列都是个人对相关技术在此时此刻的理解和研究 会根据学习深入 不断修正 但过去历史文章不见得会被 及时 修改订正 见谅 当然欢迎读者批评指正 本人虚心接受 但什么时候把文章修改正确 不好说 qe
  • 关于echarts无数据时显示问题

    我是做java开发的 最近公司让我做前端统计图表柱形图之类的 我用的就是echarts 版本是2 2 7 接下来 就说说我遇到的问题 因为我是小白 所以记录一下 ajax请求返回数据为空时显示暂无数据 echarts默认的是显示气泡 我换了
  • K-means聚类算法的应用以及实现

    K means 聚类算法属于无监督学习 它会将相似的对象归到同一个簇中 该算法原理简单 执行效率高 并且容易实现 是解决聚类问题的经典算法 尽管如此 任何一款算法都不可能做到完美无瑕 K measn 算法也有自身的不足之处 比如 K mea
  • k8s deployment 以root角色启动容器

    containers name image securityContext runAsUser 0 这样可以使容器以root用户运行 0指root用户的uid
  • C# 新增

    C 三层架构实现新增功能 第一步 用WPF实现用户界面代码 WPF中的代码就不详细说明了 现在只要和大家分享一下的是它们的后台的实现新增方法的代码 要实现我们的新增 并且新增数据 数据库必不可少 在数据库中写个新增方法的存储过程 用一个 t
  • [数学建模]MATLAB回归分析

    一 前言 第24届冬季奥林匹克运动会在北京隆重展开 我们要研究一个问题 奥运会东道主是否有优势 在历史数据中 我们发现好像东道主的国家在当年奥运会上获得金牌的数目比四年前非东道主国家获得的金牌数多 我们需要探究这个规律 二 历史背景 回归
  • 自学Python05-学会Python中的函数定义

    亲爱的同学们 今天我们将开始学习 Python 中的函数 函数就像一个魔法盒子 可以让我们在程序中执行一段代码 并且可以反复使用 这样 我们的程序就可以变得更加简洁和易于理解 现在 让我们一起来学习如何使用函数吧 首先 我们需要知道如何定义
  • Linux操作系统之进程命令和库文件

    文章目录 一 有关进程命令 1 ps命令 显示当前终端信息 2 ps ef命令 查看当前终端进程的详细信息 二 前台程序和后台程序模式的切换 1 首先了解前台后台命令 2 前后台的切换 1 kill 命令 2 进程前后台切换 三 runle
  • 第六章习题

    R1 个无线网络运行在 基础设施模式 下是什么含义 如果某网络没有运行在基础设施模式下 那么它运行在什么模式下 这种运行模式与基础设施模式之间有什么不同 在基础结构操作模式下 每个无线主机都通过基站连接到较大的网络 如果未在基础架构模式下运
  • 图灵携手全球机器学习技术大会,拥抱 AGI 变革时代

    备受行业关注的 2023 全球机器学习技术大会 ML Summit 将于10月20 21日在北京金茂威斯汀大饭店隆重举行 此次大会由 Boolan 和 CSDN 联合主办 秉承 全球专家 卓越智慧 的宗旨 特邀近 50 位人工智能与机器学习
  • 判断cuda是否安装成功

    进入环境中 输入以下命令 python import torch torch cuda is available 返回结果如下图
  • sklearn 中的线性回归、岭回归、Lasso回归参数配置及示例

    文章目录 线性回归 引入 重要参数 重要属性 重要方法 例子 岭回归 引入 重要参数 重要属性 重要方法 示例 Lasso 回归 引入 重要参数 重要属性 重要方法 示例 本文主要讲一些sklearn中回归模型的使用 如果需要了解相关理论
  • marquee的滚动属性参数

    null从
  • 泛型编程杂谈

    谈 泛型 GP 之前 先谈一下面向对象 OO OO强调世界是由对象组成的 对象是由方法和属性组成的 个人感觉还应该加上事件 而对象之间又有继承 is a 和组合等 关系 OO很符合我们认识世界的直觉 它以封装 继承和多态为特性 我们在现实工