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中的实例都是唯一的。编译原理和链接原理,如下所示:

这里写图片描述

原文地址:https://www.cnblogs.com/jinxiang1224/p/8468274.html