C++中的利器——模板

前文

本文主要是讲解一下C++中的利器——模板,相信铁子们在学完这一节后,写代码会更加的得心应手,更加的顺畅。

一,泛型编程

想要学习模板,我们要先了解为什么需要模板,我们可以看看下面这个程序。
int add(int& a, int& b)
{
    return a + b;
}
double add(double& a, double& b)
{
    return a + b;
}
看看上面的程序,通过函数重载,我们实现了不同类型的add函数,但是否有些麻烦呢,这些函数只是类型不同,而每增加一个新类型我们就要在写一个函数,而且一个出错,可能所有的函数都出错。
而我们C++的老祖宗也想到了这个问题,于是 模板就应运而生了。
通过 模板来实现我们的需求可以称为 泛型编程
泛型编程编写与类型无关的通用代码,是代码复用的一种手段。模型是泛型编程的基础
C++中的利器——模板_第1张图片

二,函数模板

2.1 函数模板的概念

函数模板代表了一个函数家族,该 函数模板与类型无关,在 使用时被参数化,根据 实参类型 产生函数的特定类型版本。

2.2函数模板格式

template
返回值类型 函数名(参数列表){}
template
A add(A& a, A& b)
{
    return a + b;
}
注意:typename是用来定义模板参数关键字,也可以使用 class(切记:不能使用struct代替class)

2.3 函数模板的原理

函数模板不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后 只是一个对函数功能框架的描述,当它具体执行时, 将根据传递的实际参数决定其功能。 所以其实就是将我们重复做的事情交给了编译器,当然要是怕累着编译器也可以自己实现,haha。
template
A add(A& a, A& b)
{
    return a + b;
}
int main()
{
    int a = 1, b = 2;
    add(a, b);

    double c = 3.3, d = 4.4;
    add(c, d);

    return 0;
}
如以上程序调用方式如下
C++中的利器——模板_第2张图片
C++中的利器——模板_第3张图片
当我们调试的时候调到反汇编,我们可以发现int型的和double型所调用的函数是不一样的,由此我们也可以论证, 编译器根据模板创建出对应函数以供使用,在这个过程中模板起到了一个蓝图的作用,而具体的建造材料则由我们传过去的参数决定。
而这个建造的过程一般是在 编译器编译阶段,对于函数模板的使用 ,编译器会根据传入的实参类型来推演出对应类型的函数以供使用。比如:当用double类型使用函数模板add时,编译器通过对参数类型的推演,将A确定为double型创造函数。

2.4 函数模板的实例化

用不同类型的参数调用函数模板,称为函数模板的 实例化。模板参数实例化分为: 隐式实例化和显示实例化

1.隐式实例化:让编译器根据实参推演模板参数的实际类型

template
A add(A& a, A& b)
{
    return a + b;
}
int main()
{
    //下面两个是比较典型的隐式实例化
    int i1 = 1, i2 = 2;
    add(i1, i2);

    double d1 = 3.3, d2 = 4.4;
    add(d1, d2);
    //那么下面这种情况呢
    //add(i1,d1) add(i2,d2)
    //这种情况下,编译器会根据实参i1将A推演为int
    //通过实参d1将A推演为double型
    //此时编译器不知道用那个,就会报错
    //那么怎么解决这种情况呢
    //1.强制转化
    add(i1, (int)d1);
    add((double)i2, d2);
    //2.显示实例化
    return 0;
}

2.显示实例化:在函数名后的<>中指定模板参数的实际类型

C++中的利器——模板_第4张图片

2.5 模板参数的匹配规则

  1. 一个非模板函数可以和一个同名函数模板同时出现,而且该函数模板还可以被实例化成这个非模板函数

int add(int& a, int& b)
{
    return a + b;
}
template
A add(A& a, A& b)
{
    return a + b;
}
int main()
{
    add(1, 2);
    //此时会优先调用非模板函数
    //因为跳过模板实例化的过程效率更高
    
    add(int)(1, 2);//通过实例化模板,
    //也可以实例化出这个非模板函数
    return 0;
}
  1. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板

int add(int& a, int& b)
{
    return a + b;
}
template
A1 add(A1& a, A2& b)
{
    return a + b;
}
int main()
{
    add(1, 2);//完全匹配非模板函数
    //不会在进行模板实例化,而是直接选择非模板函数
    add(1, 1.1);//与非模板函数不匹配,
    //模板函数会根据参数生成更加匹配的函数
    return 0;
}
  1. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

三,类模板

3.1 类模板的定义格式

定义格式
template
class 类模板名
{
    // 类内成员定义
};
template
class Stack
{
public:
    Stack(int capaicty = 4)
    {
        _a = new A[capaicty];
        _size = 0;
        _capaicty = capaicty;
    }
    ~Stack()
    {
        delete[] _a;
        _size = _capaicty = 0;
    }
private:
    A* _a;
    int _size;
    int _capaicty;
};
int main()
{
    //通过类模板,我们可以同时创建int和double类型的栈
    Stack st1;
    Stack st2;
    return 0;

}

3.2 类模板的实例化

类模板实例化与函数模板实例化不同, 类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类

总结

学习过模板后,C++的学习也会进入一段蜜月期,因为之后我们会接着学习stl库,这将大大提高我们C++代码的编写效率。

你可能感兴趣的:(C++,c++)