c++类——模版(一)

本文最后更新于:1 年前

全文仅介绍模版的基础内容,后文进行函数模版和类模版的相关实现,再介绍模版的各种特殊情况。

(一)概念:

1. 泛型编程:

  • 泛型:在计算机程序设计领域,为了避免因数据类型的不同而重复编写大量逻辑的代码,不使用具体数据类型,而是使用一种通用类型来进行程序设计的方法,大规模减少程序代码的编写量,此为泛型。

    实际上,泛型也是一种数据类型,只不过它是一种用来代替所有类型的“通用类型”。

  • 泛型编程:是不依赖于某一具体类型而使代码具有很强适应性的编程范式。

    泛型编写的代码不是真正的程序实体,只能算是一个程序实体的样板。

    泛型编程通常将算法从特定的数据结构中抽象出来,成为通用的,其编程依赖于某种形式的多态。

  • 类属:把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属。

    ——类属(参数化多态):是类型参数化,使用程序可以从逻辑功能上抽象,把被处理的对象类型也作为参数传递。

    用于表达逻辑结构相同,但具体数据根据元素类型不同的数据对象的通用行为。

2. 模板:

模板是一种对类型进行参数化的工具,模板是泛型编程的基础。

C提供了模板编程的概念。c中的模板,实际上是建立一个通用函数或类,其类内部的类型和函数的形参类型不具体指定,用一个虚拟的类型来代表。使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。

模板功能非常强大,是c中代码复用的一种常见方式,是c支持参数化多态的工具,也是许多c++标准库建立的基础,如STL的各种容器库,IO Stream等等。

3. 两者的区别:

模板是泛型的基础,泛型是模板的推广。

  • 泛型的具体类型的确定是在程序运行时,而模板的实例化是在编译时确定的;

  • 泛型无特化(自定义实现特定类型的处理),模板特化可以针对特定类型处理;

  • 泛型无部分特化,模板有;

  • 模板支持模板类型的形参(虽然一般不那样做)泛型不可以。

(二)模板的机制和格式:

  • 模板的声明和定义:只能放在全局,命名空间或者类范围内进行,即不能在局部范围或者函数内部进行,比如不能在main函数中声明和定义一个模板。

  • 工作原理:模板定义并不是真正的定义了一个函数或者类,而是编译器根据程序员缩写的模板和形参来自己写出一个对应版本的定义,这个过程叫做模板实例化。编译器成成的版本通常被称为模板的实例。编译器为程序员生成对应版本的具体过程。类似宏替换。

    模板在没有调用之前是不会生成代码的。

    由于编译器并不会直接编译模板本身,所以模板的定义通常放在头文件中。

  • 格式:

    template<class 形参名1,class 形参名2,...>

    返回类型 函数名(参数列表){函数体} /class 类名 { 类的内部定义 } ;

    template和class是关键字,形参前的class可以用typename关键字替代,一般情况下typename和class没有什么区别。

    <>里面的参数叫做模板形参,模板形参和函数形参很像,但是模板形参不能为空,一旦声明了模板就可以使用模板形参的形参名作为变量声明、作为返回类型或者作为传入参数,即可以使用内置类型的地方都可以使用模板形参名。

    模板形参在调用该模板相关内容时根据其提供的模板实参类型来初始化模板形参,也就是说一旦编译器确定了实际的模板实参类型就可以称它实例化了模板的一个实例。

  • 分类:分为函数模版和类模版。

  • 模板形参作用域:和函数形参/变量类似,是普通的作用域规则。

  • 模版实例化:

    隐式实例化:在运行期间生成实例,对于某个对象/函数调用,编译器会自动使用提供的模板实参生成具体的函数或类。一般来说,编译器也会从函数的实参中推导出模板实参。(也就是实例化的内容为<T>)

    显式实例化:在编译期间就会生成实例,对于某个对象/函数调用,明确指定T的实例化类型。(实例化的内容为<int>等具体类型)

    问题:

    在我们使用类模板时,只有当代码中使用了类模板的一个实例的名字,而且上下文环境要求必须存在类的定义时,这个类模板才被实例化。

    1. 声明一个类模板的指针和引用,不会引起类模板的实例化,因为没有必要知道该类的定义。

    2. 定义一个类类型的对象时需要该类的定义,因此类模板会被实例化。

    3. 在使用sizeof()时,它是计算对象的大小,编译器必须根据类型将其实例化出来,所以类模板被实例化.

    4. new表达式要求类模板被实例化。

    5. 引用类模板的成员会导致类模板被编译器实例化。

    6. 需要注意的是,类模板的成员函数本身也是一个模板。标准C++要求这样的成员函数只有在被调用或者取地址的时候,才被实例化。用来实例化成员函数的类型,就是其成员函数要调用的那个类对象的类型。

  • 实例化优化 :当模板被调用时才会被编译,那么就会存在这样一种情况——相同的实例化可能出现在多个文件对象中。当两个或多个独立编译地源文件适用了相同的模板,并提供了相同的模板参数时,每个文件中就都会有该模板的一个实例。

    为了控制显式实例化,可以用关键字extern显式实例化声明,这表示承诺在程序的其他位置有该实例化的一个非extern声明(定义)。

    由于编译器在使用一个模板时自动对其实例化,因此extern声明必须出现在任何用此实例化版本的代码之前。

(三)简单函数模版和类模版的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<iostream>
using namespace std;
template <typename T>
int compare(T t1, T t2) //简单的模版函数
{
if (t1 > t2)
return 1;
if (t1 == t2)
return 0;
if (t1 < t2)
return -1;
}
template <typename T>
class compare //简单的模版类
{
private:
T _val;
public:
explicit compare(T& val) : _val(val) { }
explicit compare(T&& val) : _val(val) { }
bool operator==(T& t)
{
return _val == t;
}
};

c++类——模版(一)
https://github.com/xiaohei07/xiaohei07.github.io/2023/04/14/c++类——模版(一)/
作者
07xiaohei
发布于
2023年4月14日
许可协议