c++复习之函数模板和类模板


场景:多态是用基类的成员函数,根据实际的子类对象来实现,实现不同函数的调用。每次调用都通过基类的虚函数来实现不同的对象功能。
但对于不是类对象所特有的函数,能否也实现每次调用同一函数,但类型不同,但一样可以实现相同的功能?这就是函数“模子”,也叫函数模板。函数模板只能用于 函数的参数个数相同而类型不同的情况,如果参数个数不同,则不能使用函数模板;也就是说函数模板是同一个函数名,同一个函数,可实现多个不同参数的同一功能。
若是参数个数、参数类型、参数顺序、这三者中至少要有一个不同,调用函数名相同,实现不同的功能,那叫重载。重载是同一函数名,可定义多个不同函数

函数模板

概念

设计函数时,不给出相应数据实际类型,而是将类型参数化,实际编译时,才由编译器利用实际的类型给予实例化。这样的函数就是函数模板。
模板的参数是由函数的参数推断出来的。
定义一般格式:

template <类型 参数名,类型,参数名,...>
返回值类型名 函数模板名(参数表)//参数表,即template所说明的参数名的列表
{
函数体的定义;
}
//具体定义如下:
template <class 类型参数1, class类型参数2, ...>
返回值类型  模板名(形参表)
{
    函数体
}

类型由class 或typename标识符,指明函数模板中可以接收的类型参数。参数名由标识符表示。表明函数模板中可以接收一个由“类型说明符”所规定类型的常量作为参数,到编译器编译时,会自动用具体的类型名对模板中所有的类型参数进行替换

使用例子

#include <iostream>
using namespace std;
template<class T>
void Swap(T & x, T & y)
{
    T tmp = x;
    x = y;
    y = tmp;
}
int main()
{
    int n = 1, m = 2;
    Swap(n, m);  //编译器自动生成 void Swap (int &, int &)函数
    double f = 1.2, g = 2.3;
    Swap(f, g);  //编译器自动生成 void Swap (double &, double &)函数
    return 0;
}

编译器由模板自动生成函数的过程叫模板的实例化。由模板实例化而得到的函数称为模板函数。在某些编译器中,模板只有在被实例化时,编译器才会检查其语法正确性。如果程序中写了一个模板却没有用到,那么编译器不会报告这个模板中的语法错误。

与函数不同

虽然函数模板的使用形式与函数类似但二者有本质区别:

  • 模板在编译时不会生成任何代码,只有当生成具体实例时才会生成目标代码
    • 被多个源文件引用的函数模板,应当连同函数体一同放在头文件中,而不能像普通函数那样只将声明放在头文件中。
  • 函数指针只能指向模板的实例,而不能指向模板本身

生成模板函数的过程

编译器由函数模板自动生成模板函数(实例化模板后的函数就是模板函数)会用具 体的类型名对模板中所有类型参数进行替换,同一个类型参数只能替换为同一种类型。

函数或函数模板调用语句的匹配顺序

在函数与函数模板都允许重载的情况下,当函数与函数模板名字相同情况下,调用语句如何调用呢?

  1. 先找参数完全匹配的普通函数
  2. 找不到,再找参数完全匹配的模板函数
  3. 还找不到,找实参经过自动类型转换后的能够匹配的普通函数
  4. 还找不到,报错

类模板

场景:两个或多个类,其功能是相同的,仅仅是数据类型不同,也可以使用模子来生成类

概念

类模板是对不同类的公共性质的抽象。使用泛型数据类型替代实际的数据类型来说明成员变量,从而定义一个泛型类,这相当于定义一个类的模板。
类模板需要一种或多种类型参数,所以也称为参数化类。
类模板是不同类型的事物具有相同的操作,实例化后的类之间没有联系。
声明类模板的一般格式

template<模板参数表>
calss 类模板名
{
类体定义;
}
具体定义如下:
//单个类
Template <calss T, int i>//T是类型参数,I是整型常量参数。类型参数和
						//常量参数可以是任何合法的标准类型和用户自定义类型,包括简单类型及各种结构体。
class CList
{
public:
    int SetItem(int Index, const T &Item);
    int GetItem(int Index, T &Item);
private:
    T Buffer;
};

类模板在表示如数组、表、图等数据结构显得特别重要,
这些数据结构的表示和算法不受所包含的元素类型的影响

类模板写法

  1. 可以先写一个实际的类,一般不会出错
  2. 将类中需要改变类型名的地方,改成自定义虚拟的类型名
  3. 在类前加入template<class 虚拟类型名>

类模板对象

类模板名<模板实例化参数类型> 对象名(构造函数实参表列);

类模板与模板类

类模板是C++提供的一种特殊机制,通过它我们可以定义一种特殊的类(称为模板类),
在类的定义中可以包含待定的类型参数,在声明类的实例时,系统会自动根据传递的类型生成
用户想要生成的类实例。
可见,类模板与模板类是不同的概念

使用例子

单个类模板
#include <iostream>
#include <string>
using namespace std;

template<typename T>
class A
{
public:
    A(T t)
    {
        this->t = t;
    }

    T &getT()
    {
        return t;
    }
protected:
public:
    T t;
};
int main(int argc, char const *argv[])
{
    //模板了中如果使用了构造函数,则遵守以前的类的构造函数的调用规则
    A<int>  a(100);//A后机的尖括号是告知编译器类的模板参数实例化的类型,可以与类看作一个整体,                //初始化一个类A的对象a
//    a.getT();
    cout << "a " << a.getT() << endl;
    return 0;
}
类模板派生
#include<iostream>
using namespace std;

template <typename T>
class A
{
public:
    A(T a = 0)
    {
        this->a = a;
    }
public:
    void printA()
    {
        cout << "a:" << a << endl;
    }
protected:
    T a;
private:

};
//从模板类派生时,需要具体化模板类,C++编译器需要知道父类的数据类型是什么样子的
//要知道父类所占的内存多少
class B : public A<int>
{
public:
    B(int a = 0, int b = 0): A<int>(a)
    {
        this->b = b;
    }
    void printB()
    {
        cout << "a:" << a << "b:" << b << endl;
    }
protected:
private:
    int b;

};
//从模板类派生模板类
template <typename T>
class C : public A<T>
{

public:
    C(T c, T a) : A<T>(a)
    {
        this->c = c;
    }
    void printC()
    {
        cout << "c:" << c << endl;
    }
protected:
    T c;
private:

};

int main(int argc, char const *argv[])
{
    B b(1, 2);
    b.printB();//输出a:1b:2
    C<int> c(3, 2);
    c.printC();//输出c:3
    return 0;
}

类模板函数

可以定义在类体内,自动成为内联函数。

类体外的模板函数

在类模板以外定义成员函数,采用以下形式:

template<模板参数表>
返回类型名 类模板名<模板参数标识符列表>::成员函数名(参数表)
{
函数体
}
类体外的模板函数使用例子
#include <iostream>
using namespace std;

template<int i>
class TestClass
{
public:
    int buffer[i];//使buffer的大小可变化,但其大小固定为i
    int getData(int j);
};

template<int i>
int TestClass<i>::getData(int j)
{
    return *(buffer + j);//返回的是相应位置上的值
}

int main()
{
    TestClass<6>ClassInstF;//i初始化成了6,生成一个实例classInstF,里的buffer[i]被换成buffer[6]
    int i;
    double fArr[6] = {12.1, 23.2, 34.3, 45.4, 56.5, 67.6};
    for (i = 0; i < 6; i++)
        ClassInstF.buffer[i] = fArr[i] - 10;

    for (i = 0; i < 6; i++) {
        double res = ClassInstF.getData(i);
        cout << res << " ";//输出2 13 24 35 46 57
    }
    cout << endl;
}

类模板与继承

  1. 普通类继承模板类
  2. 类模板继承普通类
  3. 类模板继承类模板
  4. 类模板继承模板类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guangod

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值