聊聊 C# 和 C++ 中的 泛型模板 底层玩法

最近在看 C++ 的方法和类模板,我就在想 C# 中也是有这个概念的,不过叫法不一样,人家叫 模板,我们叫 泛型,哈哈,有点意思,这一篇我们来聊聊它们底层是怎么玩的?

一:C++ 中的模板玩法

毕竟 C++ 是兼容 C 语言,而 C 是过程式的玩法,所以 C++ 就出现了两种模板类型,分别为: 函数模板类模板,下面简单分析一下。

1. 函数模板的玩法

玩之前先看看格式: template <typename t> rettype funcname (parameter list) { }</typename>

说实话,我感觉 C++ 这一点就做的非常好,人家在开头就特别强调了,这是一个 template,大家不要搞错了,按照这个格式,我们来一个简单的 Sum 操作,参考代码如下:


#include

//求和函数
template  T getsum(T  t1, T  t2) {
    return t1 + t2;
}

int main() {

    int sum1 = getsum(10, 10);

    long sum2 = getsum(20, 20);

    printf("output: int:sum=%d, long: sum=%ld", sum1, sum2);
}

聊聊 C# 和 C++ 中的 泛型模板 底层玩法

接下来我就很好奇,这种玩法和 &#x666E;&#x901A;&#x65B9;&#x6CD5; 调用有什么不同,要想找到答案,可以用 IDA 去看它的静态汇编代码。

聊聊 C# 和 C++ 中的 泛型模板 底层玩法

从静态反汇编代码看,当前生成了两个函数符号分别为: j_??$getsum@H@@YAHHH@Zj_??$getsum@J@@YAJJJ@Z,现在我们就搞清楚了,原来一旦给 &#x6A21;&#x677F; 指定了具体类型,它就生成了一个新的函数符号。

乍一看这句话好像没什么问题,但如果你心比较细的话,会发现一个问题,如果我调用两次 getsum<int></int> 方法,那会生成两个具体函数吗? 为了寻找答案,我们修改下代码:


int main() {

    int sum1 = getsum(10, 10);

    int sum2 = getsum(15, 15);
}

然后再用 IDA 查看一下。

聊聊 C# 和 C++ 中的 泛型模板 底层玩法

哈哈,可以发现这时候并没有生成一个新的 &#x51FD;&#x6570;&#x7B26;&#x53F7;,其实往细处说: j_??$getsum@H@@YAHHH@Z&#x51FD;&#x6570;&#x7B7E;&#x540D;组合出来的名字,因为它们签名一致,所以在编译阶段必然就一个了。

2. 类模板的玩法

首先看下类模板的格式: template <typename t1, typename t2, ...> class className { };</typename>

还是那句话,开头一个 template 暴击,告诉你这是一个模板 😄😄😄, 接下来上一段代码:


#include

template  class Calculator
{
public:
    T getsum(T a1, T b1) {
        return a1 + b1;
    }
};

int main() {

    Calculator cal1;
    int sum1 = cal1.getsum(10, 10);

    Calculator cal2;
    int sum2 = cal2.getsum(15, 15);

    printf("output: sum1=%d, sum2=%ld", sum1,sum2);
}

接下来直接看 IDA 生成的汇编代码。

聊聊 C# 和 C++ 中的 泛型模板 底层玩法

从上面的方法签名组织上看,有点意思, &#x7C7B;&#x540D;+&#x65B9;&#x6CD5;&#x540D; 柔和到一个函数符号上去了,可以看到符号不一样,说明也是根据模板实例化出的两个方法。

二:C# 中的模板玩法

接下来我们看下 C# 中如何实现 getsum 方法,当我把代码 copy 到 C# 中,我发现不能实现简单的 &#x6CDB;&#x578B;&#x53C2;&#x6570; 加减乘除操作,这就太搞了,网上找了下实现方式,当然也可以让 T 约束于 unmanaged,那就变成指针玩法了。


namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Calculator calculator1 = new Calculator();
            Calculator calculator2 = new Calculator();

            int sum1 = calculator1.getsum(10, 10);

            long sum2 = calculator2.getsum(15, 15);

            Console.WriteLine($"sum={sum1}, sum2={sum2}");
            Console.ReadLine();
        }
    }

    public class Calculator where T : struct, IComparable
    {
        public T getsum(T a1, T b1)
        {
            if (typeof(T) == typeof(int))
            {
                int a = (int)Convert.ChangeType(a1, typeof(int));
                int b = (int)Convert.ChangeType(b1, typeof(int));

                int c = a + b;
                return (T)Convert.ChangeType(c, typeof(T));
            }
            else if (typeof(T) == typeof(float))
            {
                float a = (float)Convert.ChangeType(a1, typeof(float));
                float b = (float)Convert.ChangeType(b1, typeof(float));

                float c = a + b;
                return (T)Convert.ChangeType(c, typeof(T));
            }
            else if (typeof(T) == typeof(double))
            {
                double a = (double)Convert.ChangeType(a1, typeof(double));
                double b = (double)Convert.ChangeType(b1, typeof(double));

                double c = a + b;
                return (T)Convert.ChangeType(c, typeof(T));
            }
            else if (typeof(T) == typeof(decimal))
            {
                decimal a = (decimal)Convert.ChangeType(a1, typeof(decimal));
                decimal b = (decimal)Convert.ChangeType(b1, typeof(decimal));

                decimal c = a + b;
                return (T)Convert.ChangeType(c, typeof(T));
            }

            return default(T);
        }
    }
}

那怎么去看 Calculator<int></int>Calculator<long> </long> 到底变成啥了呢? 大家应该知道,C# 和 操作系统 隔了一层 C++,所以研究这种远离操作系统的语言还是有一点难度的,不过既然隔了一层 C++ ,那在 C++ 层面上必然会有所反应。

如果你熟悉 CLR 的类型系统,应该知道 C# 所有的 类 在其上都有一个 MethodTable 类来承载,所以它就是鉴别我们是否生成多个个体的依据,接下来我们用 WinDbg 查看托管堆,看看在其上是如何呈现的。


0:008> !dumpheap -stat
Statistics:
              MT    Count    TotalSize Class Name
00007ff9d37638e0        1           24 ConsoleApp1.Calculator1[[System.Int64, System.Private.CoreLib]]
00007ff9d3763800        1           24 ConsoleApp1.Calculator1[[System.Int32, System.Private.CoreLib]]

从输出信息看,C++ 层面变成了两个 methodtable 类,如果不信的化,还可以分别查看 mt 下的所有方法。

`c#

0:008> !dumpmt -md 00007ff9d37638e0
MethodDesc Table
Entry MethodDesc JIT Name

00007FF9D36924E8 00007ff9d37638d0 JIT ConsoleApp1.Calculator1[[System.Int64, System.Private.CoreLib]]..ctor()
00007FF9D36924E0 00007ff9d37638c0 JIT ConsoleApp1.Calculator
1[[System.Int64, System.Private.CoreLib]].getsum(Int64, Int64)

0:008> !dumpmt -md 00007ff9d3763800

Original: https://www.cnblogs.com/huangxincheng/p/16384668.html
Author: 一线码农
Title: 聊聊 C# 和 C++ 中的 泛型模板 底层玩法

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/536383/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • C++快速入门系列教程

    移动开发工程师 。涉及 android、ios、jni Original: https://www.cnblogs.com/xitang/p/4176128.htmlAuthor:…

    C++ 2023年5月29日
    087
  • Fixed width integer types (since C++11)

    越界问题非常频繁地困扰着开发人员 CPP常用的类型定义不能体现数据的bit位数,让开发人员非常抓狂,相信很多人都遇到过,当判定一个数值是否越界时候,一定是先去网上查表, 但是数据类…

    C++ 2023年5月29日
    077
  • c++如何遍历删除map/vector里面的元素

    新技能Get! 对于c++里面的容器, 我们可以使用iterator进行方便的遍历. 但是当我们通过iterator对vector/map等进行修改时, 我们就要小心了, 因为操作…

    C++ 2023年5月29日
    096
  • C++ 相关库

    C++ 相关库 说明 RapidJSONhttps://github.com/Tencent/rapidjson Original: https://www.cnblogs.com…

    C++ 2023年5月29日
    071
  • C++ 回调函数详解

    1、什么是回调函数回调函数本质上也是普通函数,只是调用机制有所区别——首先通过传参的形式将该函数的地址传递给其他函数,然后在其他函数中通过函数指针调用该函数。在其他函数中通过函数指…

    C++ 2023年5月29日
    077
  • 收藏的博客 — Qt/C++学习

    Qt Creator环境: 使用Qt Creator作为Linux IDE,代替Vim:实现两台Linux电脑远程部署和调试(一台电脑有桌面系统,一台电脑无桌面系统) 使用Qt C…

    C++ 2023年5月29日
    050
  • C/C++标准新特性简介

    参考文档 C语言的起源发展 C语言诞生于1972年,美国贝尔实验室。作者为:Dennis MacAlistair Ritchie(丹尼斯·里奇) & Kenneth Lan…

    C++ 2023年5月29日
    087
  • 【C++服务端技术】消息队列

    ThreadWorkUnit.h #pragma once #include #include #include "SafeQueue.h" namespace…

    C++ 2023年5月29日
    060
  • 转:TinyXM–优秀的C++ XML解析器

    include include “tinyxml.h” include “tinystr.h” include include in…

    C++ 2023年5月29日
    055
  • C#与c++对应的类型

    C#与c++对应的类型 csharp;gutter:true; C#调用C++的DLL搜集整理的所有数据类型转换方式-转载</p> <pre><cod…

    C++ 2023年5月29日
    047
  • (转)C/C++中计算程序运行时间

    以前经常听人提起如何计算程序运行时间,给出一系列函数,当时没有注意,随便选了clock()最简单的方式进行计算。等到真正需要检测程序性能提升了多少,才发现这里面有很多要注意的地方。…

    C++ 2023年5月29日
    063
  • The main difference between Java & C++(转载)

    C++ supports pointers whereas Java does not pointers. But when many programmers questioned…

    C++ 2023年5月29日
    070
  • C++ 使用静态链接库和动态链接库

    C++ 使用AdbHelper静态链接库 ## 必备文件:静态库AdbHelper.h,AdbHelper.lib以及动态库AdbHelper.dll ##   资源文件使用用法:…

    C++ 2023年5月29日
    058
  • 无废话–Mac OS, VS Code 搭建c/c++基本开发环境

    无废话,直接上步骤。 1) 安装 xcode。 打开App Store,搜索xcode,进行下载安装。 2)执行命令: xcode-select –install 安装…

    C++ 2023年5月29日
    066
  • [C++] const和mutable关键字使用方法

    const 修饰的变量为常成员变量,表示此变量不能被修改赋值,并且构造函数中只能用初始化列表的方式初始化,其他初始化方式都是错误的 const 修饰的函数为常成员函数,表示此函数中…

    C++ 2023年5月29日
    070
  • 玩转cocos2d-x lua-binding, 实现c++与lua混合编程

    引言 城市精灵GO(http://csjl.teamtop3.com/)是一款基于cocos2d-x开发的LBS社交游戏, 通过真实地图的探索, 发现和抓捕隐匿于身边的野生精灵, …

    C++ 2023年5月29日
    057
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球