• C++模板



    用于算法抽象的模板

    下面 swapValues 只支持 int 类型:

    void swapValues(int& var1, int& var2)
    {
        int temp;
    
        temp = var1;
        var1 = var2;
        var2 = temp;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如果我们想将 swapValues 函数用于 char 变量,可添加以下定义来重载函数名:

    void swapValues(char& var1, char& var2)
    {
        char temp;
    
        temp = var1;
        var1 = var2;
        var2 = temp;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    但这样做明显效率很低,令人不满意,如果要将 swapValues 函数应用于更多类型,必须一次又一次地重复几乎完全一样的函数定义。这会使得代码充斥着大量几乎完全一样的定义。我们认为,以下函数定义适合任何类型的变量。

    // 交换var1和var2的值
    template<class T>
    void swapValues(T& var1, T& var2)
    {
        T temp;
    
        temp = var1;
        var1 = var2;
        var2 = temp;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其中 template 这通常称为 模板前缀,它告诉编译器,后续的定义或函数声明是 模板T类型参数

    下面是这个实例的全部演示代码:

    // 该程序用于演示一个函数模板
    #include 
    using namespace std;
    
    // 交换var1和var2的值
    template<class T>
    void swapValues(T& var1, T& var2)
    {
        T temp;
    
        temp = var1;
        var1 = var2;
        var2 = temp;
    }
    
    int main()
    {
        int integer1 = 1, integer2 = 2;
        cout<<integer1<<" "<<integer2<<endl; // 1 2
        swapValues(integer1, integer2);
        cout<<integer1<<" "<<integer2<<endl; // 2 1
    
        char symbol1 = 'A', symbol2 = 'B';
        cout<<symbol1<<" "<<symbol2<<endl; // A B
        swapValues(symbol1, symbol2);
        cout<<symbol1<<" "<<symbol2<<endl; // B A
    	
    	return 0;
    }
    
    • 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
    • 26
    • 27
    • 28
    • 29

    编译器不会真的为函数名称 swapValues 生成针对所有可能类型的定义,只是表现得像是生成了所有函数定义。针对用到的每种类型,编译器都生成单独的函数定义,但不会为没用到的任何类型生成定义。另外,无论为一种类型使用多少次模板,都只为那种类型生成一个定义。

    此外,函数模板也可能有多个类型参数。例如,具有两个类型参数(T1T2)的一个函数模板可以像下面这样开头:

    template<class T1, class T2>
    
    • 1

    但大多数函数模板都只需一个类型参数。不能有未使用的模板参数。换言之,指定的每个模板参数都必须在模板函数中用到。

    算法抽象是指用一个更常规(泛化)的方式表示算法,忽略完全一致的细节,将重点放在算法本质上。C++设计了许多功能来支持算法抽象,函数模板是其中之一。

    下面使用泛型排序函数:

    // 该程序用于演示一个函数模板
    #include 
    using namespace std;
    
    // 交换var1和var2的值
    template<class T>
    void swapValues(T& var1, T& var2)
    {
        T temp;
    
        temp = var1;
        var1 = var2;
        var2 = temp;
    }
    
    template<class BaseType>
    int indexOfSmallest(const BaseType a[], int startIndex, int numberUsed)
    {
        BaseType min = a[startIndex];
        int indexOfMin = startIndex;
    
        for (int index = startIndex + 1; index < numberUsed; index++)
            if (a[index] < min)
            {
                min = a[index];
                indexOfMin = index;
                // min是[startIndex]到a[index]之间最小的元素
            }
    
        return indexOfMin;
    }
    
    template<class BaseType>
    void sort(BaseType a[], int numberUsed)
    {
        int indexOfNextSmallest;
        for(int index=0; index<numberUsed-1; index++)
        {
            // 将之前的值防在a[index]中
            indexOfNextSmallest = indexOfSmallest(a, index, numberUsed);
            swapValues(a[index], a[indexOfNextSmallest]);
        }
    }
    int main()
    {
        int a[10] = {9,8,7,6,5,1,2,3,0,4};
        for(int i=0; i<10; ++i)
            cout << a[i] << " ";
        cout<<endl;
        sort(a, 10);
        for(int i=0; i<10; ++i)
            cout << a[i] << " ";
        cout<<endl;
    
        double b[5] = {5.5, 4.4, 1.1, 3.3, 2.2};
        for(int i=0; i<5; ++i)
            cout << b[i] << " ";
        cout<<endl;
        sort(b, 5);
        for(int i=0; i<5; ++i)
            cout << b[i] << " ";
        cout<<endl;
    
        char c[7] = {'G', 'E', 'N', 'E', 'R', 'I', 'C'};
        for(int i=0; i<7; ++i)
            cout<<c[i]<<" ";
        cout<<endl;
        sort(c, 7);
        for(int i=0; i<7; ++i)
            cout<<c[i]<<" ";
        cout<<endl;
        return 0;
    }
    
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    如果要使用这个泛型,那么这种类型得可以用 < 比较。比如下面就是未不恰当的类型使用模板:

    int a[10], b[10];
    <用于填充数组的一些代码>
    swapValues(a, b);
    
    • 1
    • 2
    • 3

    上述代码不能工作,因为数组类型不支持赋值。


    用于数据抽象的模板

    类模板的语法和函数模板基本一样。下面是放在模板定义之前的语句:

    template<class T>
    
    • 1

    类型参数 T 在类定义中的用法与其他任何类型无异。和函数模板一样,类型参数 T 可以是任何类型;类型参数不一定要替换成类类型。和函数模板一样,可用任何(非关键字)标识符代表 T

    以下面的类模板为例。该类的对象包含一对 T 类型的值。 如果 Tint,则对象值是一对整数;如果 Tchar,则对象值是一对字符,依此类推。

    // 该类表示一对T类型的值:
    template<class T>
    class Pair
    {
    public:
    	Pair();
    	Pair(T firstValue, T secondValue);
    	
    	void setElement(int position, T value);
    	// 前条件:position是1或2
    	// 后条件:指定的 position 被设置成 value
     	
     	T getElement(int position) const;
     	// 前条件:position 是 1 或 2
     	// 返回在指定 position 的值
    private:
    	T first;
    	T second;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    定义好类模板之后就可声明该类的对象。声明必须指定要为 T 填充什么类型。例如,以下语句声明 score 对象来记录一对整数。另外还声明 seats 对象来记录一对字符:

    Pair<int> score;
    Pair<char> seats;
    
    • 1
    • 2

    然后可以像使用其他任何对象那样使用上述两个对象。例如,以下语句将第一个球队的 score 设置为3,将第二个球队的 score 设置成0:

    score.setElement(1, 3);
    score.setElement(2, 0);
    
    • 1
    • 2

    类模板的成员函数采用与普通类的成员函数一样的方式来定义。唯一的区别是,成员函数定义本身也是模板。例如,以下代码给出了成员函数 setElement 的一个恰当的定义,共给出了接收两个参数的构造函数的定义:

    // 使用iostream和cstdlib
    template<class T>
    void Pair<T>::setElement(int position, T value)
    {
    	if (positon == 1)
    		first = value;
    	else if (position == 2)
    		second = value;
    	else
    	{
    		cout << "Error: Illegal pair position.\n";
    		exit(1);
    	}	
    }
    
    template<class T>
    Pair<T>::Pair(T firstValue, T secondValue)
    		: first(firstValue), second(secondValue)
    {
    	// 主体有意留空
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    注意,作用域解析操作符之前的类名是 Pair,而非单单是 Pair

    类模板名称可用作函数参数类型。例如,以下语句声明一个函数,参数是一对整数:

    int addUp(const Pair<int>& thePair);
    // 返回thePair中的两个整数的和
    
    • 1
    • 2

    注意,其中指定了要填充的类型(本例是 int),它将取代类型参数 T

    甚至可以在函数模板中使用类模板。例如,可以不像上面那样定义具体的 addUp 函数,而是定义函数模板,使函数能支持所有数值类型:

    template<class T>
    T addUp(const Pair<T>& thePair);
    // 前条件已经为T类型的值定义了操作符+
    // 返回thePair中的两个值之和
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    access-list vs ip access-list
    「UG/NX」Block UI 超级点SuperPoint
    聊聊 flink 的时间窗口
    C#,有向无环图(DAG,Directed Acyclic Graph)的最短路径(Shortest Path)算法与源代码
    EDA 虚拟机 Synopsys Sentaurus TCAD 2017.09 下载
    如何执行建设项目的时间影响分析?
    微信小程序毕业设计-英语互助系统项目开发实战(附源码+论文)
    使用easyexcel模板导出的两个坑(Map空数据列错乱和不支持嵌套对象)
    qt5-入门-自定义委托-可编辑的TableModel与信号接收
    Open-source software (OSS)
  • 原文地址:https://blog.csdn.net/weixin_44491423/article/details/126216223