zoukankan      html  css  js  c++  java
  • C++ 模板的编译与链接

    模板是C++泛型编程的基础,一个模板就是一个创建类或者函数的公式;在具体使用时,我们需要将模板参数转化为具体类型,比如int,float,Vector以及自定义类型;这种转换过程发生在编译阶段。

    定义模板

    假如我们需要完成一个数值幂次方函数的功能,这个数组可以是int,也可以是float;我们的函数分别如下:

    整型值的幂次函数:

    int Power(int base, int index)
    {
        if (0 == index) return (int)1;
        if (0 > index) return(int)0; 
    
        int result =  base;
        while (--index)
        {
            result *= base;
        }
    
        return result;
    }

    浮点型的幂次函数:

    float Power(float base, int index)
    {
        if (0 == index) return (float)1;
        if (0 > index) return(float)0; 
    
        float result =  base;
        while (--index)
        {
            result *= base;
        }
    
        return result;
    }

    可以发现这两个函数基本一样,唯一的差异就是参数和返回值类型不同,函数体完全一致;若还需要针对long型,long long型,我们需要写两份几乎一样的代码。

    针对这种情况,C++给出了模板技术,代码编写阶段类型先不确定;在编译器编译时根据函数入参类型再确定,从而生成具体的函数版本。

    函数模板

    函数模板定义

    函数模板的定义如下,由template关键字,一对尖括号组成,T是由typename 标识的不确定类型,编译器会在编译阶段生成具体的函数版本。

    template<typename T>
    T Power(T base, int index)
    {
      if (0 == index) return (T)1;
      if (0 > index)  return(T)0;
    
      T result =  base;
      while (--index)
      {
         result *= base;
      }
      return result;
    }

    实例化函数模板

    当我们调用一个函数模板时,编译器会根据函数实参来推断模板的具体类型,然后根据这个类型实例化这个类型的版本函数。比如:

    (1) cout << "power(2,3):" << Power(2,3) << endl;
    (2) cout << "power(2.1,3):" << Power(2.1f,3) << endl;

    编译器在编译时,遇到语句(1),可以推断出模板实参的类型为int,编译器则开始生成入参为int型的函数版本;同理,遇到语句(2)则生成入参为float型的函数版本;
    它们的函数声明如下:

    (版本一)int Power(int base, int index)
    (版本二)float Power(float base, int index)

    类模板

    类模板(Class template)是用来生成类的蓝图,其原理基本和函数模板基本一致;所不同的是,使用类模板时,我们必须显示指定类模板的类型信息。

    类模板定义:

    类模板的定义如下:

    template <typename T>
    class CThree
    {
    public:
        CThree(T a, T b, T c) ;
        T Min();
        T Max();
    private:
        T m_a;
        T m_b;
        T m_c;
    };

    类模板实现:

    需要注意的是, 每个成员函数前面都要加上 template< class T >,而且类的名称应该使用CThree< T >.示例代码如下:

    template<class T>
    T CThree<T>::Min()
    {
        T MinVal = m_a < m_b ? m_a : m_b;
    
        return MinVal < m_c ? MinVal :m_c;
    }
    template<class T>
    T CThree<T>::Max()
    {
        T MaxVal = m_a > m_b ? m_a : m_b;
    
        return MaxVal > m_c ? MaxVal :m_c;
    }
    
    template<class T>
    CThree<T>::CThree(T a, T b, T c):
    m_a(a),
    m_b(b),
    m_c(c)
    {
        return;
    }

    实例化类模板

    类模板和函数模板一样,也存在实例化的过程,当编译器遇到语句(1)(2)时,编译器则生成int型和float型的类版本,这两个版本是相互独立的类,不会相互影响。

    (1) CThree<int> IntInstance(1,2,4);
    (2) CThree<float> FloatInstance(1.1f,2.2f,4.4f);

    模板编译与链接

    现在就来看看,编译器对模板是如何编译和链接吧;

    当编译器遇到一个template时,不能够立马为他产生机器代码,它必须等到template被指定某种类型。也就是说,函数模板和类模板的完整定义将出现在template被使用的每一个角落,比如遇到上述中的4个语句时,才能确定编译内容,否则编译器没有足够的信息产生机器代码。

    对于不同的编译器,其对模板的编译和链接技术也会有所不同,其中一个常用的技术称之为Smart,其基本原理如下:

    1. 模板编译时,以每个cpp文件为编译单位,实例化该文件中的函数模板和类模板
    2. 链接器在链接每个目标文件时,会检测是否存在相同的实例;有存在相同的实例版本,则删除一个重复的实例,保证模板实例化没有重复存在。

    比如我们有一个程序,包含A.cpp和B.cpp,它们都调用了CThree模板类,在A文件中定义了int和double型的模板类,在B文件中定义了int和float型的模板类;在编译器编译时.cpp文件为编译基础,生成A.obj和B.obj目标文件,即使A.obj和B.obj存在重复的实例版本,但是在链接时,链接器会把所有冗余的模板实例代码删除,保证exe中的实例都是唯一的。编译原理和链接原理,如下所示:

    这里写图片描述

  • 相关阅读:
    还是this的问题
    this的问题
    javascript深入理解js闭包
    立即执行函数: (function(){...})() 与 (function(){...}()) 有什么区别?
    java中的精度问题的处理
    51nod 1766 树上的最远点对——线段树
    CODE FESTIVAL 2017 qual B C
    bzoj 2144: 跳跳棋——倍增/二分
    洛谷八连测第一轮
    bzoj 2079: [Poi2010]Guilds——结论题
  • 原文地址:https://www.cnblogs.com/jinxiang1224/p/8468274.html
Copyright © 2011-2022 走看看