STL与模板

一、如何进行泛型编程
     C/C++ 是一种静态编程语言,必须需要把代码翻译成可执行的二进制可执行程序然后在运行,一旦编译好之后就不能再变了(数据类型也就必须确定下无法更改,因此要为每一种数据类型编写一份算法,工程量巨大)。

  C++提供了模板的编程方法来解决泛型编程的问题,它的解决思路是,程序员先编写好一份"套路"代码,然后在调用时编译器根据调用时的参数再为这种数据类型生成一份属于它的代码。

  C语言中也有类似的模板,比如快速排序。

 void qsort(void *base, size_t nmemb, size_t size,int(*compar)(const void *, const void *));

  以下是这个模板的应用:

 int intcmp(const void* p_1,const void* p_2)
    {
        int* p1 = p_1;
        int* p2 = p_2;
        if(*p1 == *p2) return 0;
        else(*p1 > *p2) return 1;
        else return -1;
    }
    int main()
    {
        int arr[10]={9,5,4,2,3,1,6,8,7,0};
        qsort(arr,10,sizeof(arr[0]),intcmp);
    }
    

注意点:这里的第4个参数的函数是需要我们自己提供的,也就是需要我们自己提供排序的规则。

二、C++中版本的语法

  

template <typename T,typename M ....>
    T max(T num1,T num2)
    {
        return num1 > num2 ?num1:num2;
    }

这里的T和M就是模板的参数类型,可以叫任何名字,只是约定俗成叫做T,它指的是函数调用时的任何类型的参数。

这个模板的使用,以下用冒泡排序举个例子:

#include <iostream>
#include <stdio.h>
#define swap(a,b) {typeof(a) t=a;a=b;b=t;}

using namespace std;
template <typename T>
void bubble_sort(T* arr,int size)
{
    for(int i=size -1;i>=0;i--)
    {
        for(int j=0;j<i;j++)
        {
            if(arr[j] > arr[j+1])
            {
                swap(arr[j],arr[j+1]);    
            }
        }
    }
}
int main()
{
//    short arr[10] ={6,5,7,8,2,9,1,0,3,4};
//  char arr[10] ={'6','5','7','8','2','9','1','0','3','4'};
    char arr[10] ="absndkiro";
    for(int i =0;i<10;i++)
    {
        cout << arr[i] <<endl;
    }
}

三、函数模板的使用
    1、模板的实例化
        编译器不会把函数模板编译成一个实例,而是根据调用的参数,再进行实例化(进一步生成二进制指令)。
       
    2、使用模板时才实例化
        模板只有在调用时才会实例化,因此模板编译正确并不代表代码没有问题,很多错误会产生于调用时。
       
    3、二次编译
        第一次编译是检查模板的语法,第二次编译是根据调用参数把模板实例化出来再检查运算符是否支持这种类型。

四、函数模板的隐式推断
    a、使用函数模板时可以根据参数的类型来推断模板的参数。
   
    b、当函数模板不能通过函数调用时的参数来推断模板参数时,可以使用<类型,类型...> 来明确指定。
   
    c、函数的模板参数可以有默认值。
        1、默认值优先放在右边
        2、C++标准才支持  : -std=c++0x

六、类模板
    a、类模板的语法
    类模板的参数可以在类中当作类型使用,可以定义成员、返回值、参数等。
    template<class T,class A,class B...>
    class className
    {
        C c;
    public:
        T func(A a);
    }
    注意:typename也可以继续使用,但大多用class以示区别。


   
    b、类模板的使用
    类模板必须要经过实例化才能使用,也是需要经过两次编译,第一次是把类模板编译成一个"套路",这个过程是为了检查语法,第二次是根据实例化参数,生成一个类,然后才能使用这个类创建对象。
   
    使用类模板实例化一个类:
    className<type1,type2...> a;

 以下举个例子,实现栈类模板:

#include <iostream>

using namespace std;

template<class T>
class Mystack
{
    struct Node
    {
        T data;
        Node* next;
        Node(T data):data(data),next(NULL)
        {}
    };
    Node* top;
public:
    Mystack(void)
    {
        top = NULL;    
    }
    bool empty_stack(void)
    {
        return top == NULL;    
    }
    void push_stack(T data)
    {
        Node* node = new Node(data);
        node->next =top;
        top = node;
        cout << data << endl;
    }
   bool pop_stack()
    {
        if(empty_stack()) return false;
        Node* temp = top;
        top = top->next;
        cout << temp->data << endl;
        delete temp;
        return true;
    }

};
int main()
{
    Mystack<int> stack;
    //Mystack<int>* stack = new Mystack<int>;
    for(int i=0;i<10;i++)
    {
        stack.push_stack(i);    
    }
    for(int i=0;i<10;i++)
    {
        stack.pop_stack();
    }
}


    c、类模板参数不支持隐式推断,必须显式实例化
   
    d、静态成员的定义
    template<class T> int MyStack<T>::num =10;
    静态成员必须是在类模板实例化之后才被真正定义处理,每一个实例化的类都有一份静态成员,这个实例化类创建出的对象共用一个静态成员。
    e、递归实例化
        Mystack<Mystack<int>> stack;
        尽量不要轻易使用

七、类模板的特化(重载)
    特化:指的是当类模板有特殊的类型无法处理时,可以为这种类型单独实例化出一个类,这种单独的实现叫做模板的特化。
    全类的特化:按照类的格式把类完整再实现一遍(重写一遍)。
    template<> class className<char*>
    {
       
    };
   
    成员特化:给指定的类型提供一个特殊的成员函数。
    template<> 返回值 className<char*>:: max(void)
    {
        ...
    }
    局部特化:可以让用户根据实例化时的参数来指定使用的类模板
   
    注意:同等程序的特化会引起二义性
   
八、类模板的参数
    1、类模板的参数可以有默认值。
        注意:使用类模板默认值时<>必须加上,可以空着,但不能不写。
    2、类模板后面的参数可以调用前面的参数。
   
    3、普通数值可以对类模板进行实例化,它必须以类似变量的形式存在。
    注意:只能是常量才能进行实例化。

原文地址:https://www.cnblogs.com/yyc954330712/p/9532887.html