C++ 模板

模板

泛型(Generic Programming),即是指具有在多种数据类型上皆可操作的含意。泛型
编程的代表作品 STL 是一种高效、泛型、可交互操作的软件组件。

泛型编程最初诞生于 C++中,目的是为了实现 C++的 STL(标准模板库)。其语言
支持机制就是模板(Templates)。

模板的精神其实很简单:类型参数化(type parameterized),即,
类型也是一种参数,也是一种静多态。

换句话说,把一个原本特定于某个类型的算法或类当中的类型信息抽掉,抽出来做成模板参数 T。

重载函数,固然实现了泛化的一面,但是不彻底,且有二义性的存在

函数模板

//在一个函数的参数表,返回类型和函数体中使用参数化的类型。
template<typename/class 类型参数 T1,typename/class 类型参数 T2,...>
返回类型 函数模板名(函数参数列表)
{
    函数模板定义体
}
#if 1
#include <iostream>
using namespace std;

class MyType{
    public:
        MyType(int x, int y): _x(x), _y(y){};
        int _x;
        int _y;
};
template<typename T>
void myswqp(T &a, T &b){
   T temp = a;
   a = b;
   b = temp;
}

int main(){
    int a = 10, b = 20;
    myswqp<int>(a, b);
    cout << "a = " << a << " b = " << b << endl; //a = 20 b = 10

    MyType obj1(10, 20);
    MyType obj2(30, 40);
    myswqp<MyType>(obj1, obj2);


    cout << "obj1._x = " << obj1._x << " obj1._y = " << obj1._y << endl; //obj1._x = 30 obj1._y = 40
    cout << "obj2._x = " << obj2._x << " obj2._y = " << obj2._y << endl; //obj2._x = 10 obj2._y = 20
    return 0;
}


#endif

模板参数作返回类型

特性

先实例化,再调用。
严格匹配,不存在隐式转化。

模板编译原理

编译器遇到模板方法定义时,会进行语法检查,但是并不编译模板。
编译器无法编译模板定义,因为它不知道使用什么类型。

比如 T a,b; 在 不知晓 T 具体类型时,是
无法分配内存,更无从谈编译 a = b;

T 获取类型的过程,称为模板的实例化,
函数模板通过具体类型产生不同的函数;
编译器会对函数模板进行两次编译:
在声明的地方对模板代码本身进行编译(类型检查),
在调用的地方对参数替换后的代码进行编译(代码生成)。

函数模板应用

数据类型的泛化,使的模板特别适合于作算法的抽象。STL 中的 algorithm 就是
典型的代表。比如,算法 sort 就是支持类型泛化的代表排序算法。

void quickSort(T * array,int left, int right) 将快速排序算法实现模板化。


#include <iostream>
#include <typeinfo>
using namespace std;
template<typename T>
void quickSort(T * array,int left, int right)
{
    if(left<right)
    {
        int low = left; int high = right;
        T pivot = array[low];
        while(low<high)
        {
            while(array[high] >= pivot && high>low )
                high--;
            array[low] = array[high];
            while(array[low] <= pivot&& high>low)
                low++;
            array[high] = array[low];
        }
        array[low] = pivot;
        quickSort(array,left,low-1);
        quickSort(array,low+1,right);
    }
}
int main()
{
    int array[10] = {1,3,5,7,2,4,6,8,0,9};
    quickSort<int>(array,0,9);
    for(auto i:array)
    {
        cout<<i<<endl;
    }
}

函数模板的特化

Template Specialization,函数模板的特化,即函数模板的特性情况,个例行
为。

就是在实例化模板时,对特定类型的实参进行特殊处理,即实例化一个特殊的实例
版本。

#include <iostream>
#include <string.h>
using namespace std;
template<typename T> int compare( T &a, T &b)
{
    if(a > b) return 1;
    else if(a < b)return -1;
    else return 0;
}

//实参为两个 char*时,比较的是指针的大小,
//而不是指针指向内容的大小,此时就需要为该函数模板定义一个特化版本,即特殊处理的版本:
template<> int compare < const char * >( const char*  &a, const char*  &b)
{
    cout<<"特化的函数模板执行了"<<endl;
    return strcmp(a,b);
}

int main()
{
    int a = 3; int b = 5;
    cout<<compare(a,b)<<endl; // -1
    string str1 = "abc",str2 ="abc";
    cout<<compare(str1,str2)<<endl; //0
    //char * 与 const char* 亦属于不匹配
    const char * p1 = "abc",*p2= "def";
    cout<<compare(p1,p2)<<endl;//-1
    cout<<compare(p2,p1)<<endl;//1
    return 0;
}


输出:
-1
0
特化的函数模板执行了
-1
特化的函数模板执行了
1

函数模板适用场景

函数模板,只适用于函数的参数个数相同而类型不同,且函数体相同的情况。

如果个数不同,则不能用函数模板。

类模板

Stack 类模板化,可以 push 和 pop 不同的数据类型。主要由几个因素需要把控。
栈中的空间元素类型,压入元素类型,弹出元素类型,三者保持一致即可。

# if 1
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;


template<typename T>
class Stack
{
public:
    Stack(int size=1024);
    ~Stack();
    bool isEmpty();
    bool isFull();
    void push(T data);
    T pop();
private:
    T* space;
    int top;
};

template<typename T> Stack<T>::Stack(int size)
{
    space = new T[size];
    top = 0;
}
template<typename T> Stack<T>::~Stack(){
    delete []space;
}
template<typename T> bool Stack<T>::isEmpty(){
    return top == 0;
}

template<typename T> bool Stack<T>::isFull(){
    return top == 1024;
}
template<typename T> void Stack<T>::push(T data){
    space[top++] = data;
}
template<typename T> T Stack<T>::pop(){
    return space[--top];
}


int main()
{
    Stack<int> s(100);
    for(int i=0; i<10; ++i){
        if(!s.isFull())
            s.push(i);
    }
    while(!s.isEmpty())
        cout<<s.pop()<<endl;
    return 0;
}
# endif
输出
9
8
7
6
5
4
3
2
1
0

类模板的友元

hpp

由于编译器需要通过这些"模板"为实例化类型生成实际的方法代码,因此任何使
用了模板的源代码文件中,编译器都应该能同时访问类模板定义和方法定义。

C++中的编译是以文件为单位的,然后链接阶段完成链接。

如果模板的声明与实现分开,这种机制显然会导致看不到模板的全貌,而致编译失败。

所以常将类模板定义和方法定义放到一起,该类模板文件的后缀常为.hpp,以示与普通文件的区别。

在使用的时候,#include"xx.hpp"。

mylist.hpp

#ifndef __MYLIST_HPP__
#define __MYLIST_HPP__

#include <iostream>
using namespace std;

// 定义命名空间 listspace
namespace listspace {
    // 声明模板类 GenericList
    template<class ItemType>
    class GenericList;

    // 声明友元函数 operator<< 用于输出 GenericList 对象
    template<class ItemType>
    ostream& operator<<(ostream& outs, const GenericList<ItemType>& the_list);

    // 定义模板类 GenericList
    template<class ItemType>
    class GenericList {
    public:
        // 构造函数,初始化列表的最大长度
        GenericList(int max);

        // 析构函数,释放动态分配的内存
        ~GenericList();

        // 返回当前列表的长度
        int length() const;

        // 向列表中添加新元素
        void add(ItemType new_item);

        // 判断列表是否已满
        bool full() const;

        // 清空列表
        void erase();

        // 声明友元函数 operator<<,用于输出 GenericList 对象
        friend ostream& operator<< <>(ostream& outs, const GenericList<ItemType>& the_list);

    private:
        // 存储列表元素的数组指针
        ItemType* _item;

        // 列表的最大长度
        int _maxLength;

        // 当前元素的索引
        int _curIdx;
    };

    // 定义 GenericList 类的构造函数
    template<class ItemType>
    GenericList<ItemType>::GenericList(int max)
        : _maxLength(max), _curIdx(0) {
        _item = new ItemType[max];
    }

    // 定义 GenericList 类的析构函数
    template<class ItemType>
    GenericList<ItemType>::~GenericList() {
        delete[] _item;
    }

    // 定义返回当前列表长度的成员函数
    template<class ItemType>
    int GenericList<ItemType>::length() const {
        return _curIdx;
    }

    // 定义向列表中添加新元素的成员函数
    template<class ItemType>
    void GenericList<ItemType>::add(ItemType new_item) {
        if (full()) {
            cout << "Error: adding to a full list.\n";
            exit(1);
        } else {
            _item[_curIdx] = new_item;
            _curIdx++;
        }
    }

    // 定义判断列表是否已满的成员函数
    template<class ItemType>
    bool GenericList<ItemType>::full() const {
        return _curIdx == _maxLength;
    }

    // 定义清空列表的成员函数
    template<class ItemType>
    void GenericList<ItemType>::erase() {
        _curIdx = 0;
    }

    // 定义友元函数 operator<<,用于输出 GenericList 对象
    template<class ItemType>
    ostream& operator<<(ostream& outs, const GenericList<ItemType>& the_list) {
        for (int i = 0; i < the_list._curIdx; i++) {
            outs << the_list._item[i] << endl;
        }
        return outs;
    }
}

#endif

main.cpp


#include <iostream>
#include "mylist.hpp"
using namespace std;
using namespace listspace;
int main( )
{
    GenericList<int> first_list(2);
    first_list.add(1);
    first_list.add(2);
    cout << "first_list = \n"
         << first_list;
    GenericList<char> second_list(10);
    second_list.add('A');
    second_list.add('B');
    second_list.add('C');
    cout << "second_list = \n"
         << second_list;
    return 0;
}

输出:

first_list =
1
2
second_list =
A
B
C

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

可能只会写BUG

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

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

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

打赏作者

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

抵扣说明:

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

余额充值