模板

1.函数模板

1.模板概念 

 当一个函数进行的操作对很多类型都适用时,我们没必要重载多个函数。可以定义一个通用

的函数模板。看一个栗子

template <typename T>
bool compare(const T &val1,const T &val2)
{
    if(val1 > val2) return true;
    return false;
}
int main()
{
    cout << boolalpha << compare(34,90) << endl;
    cout << boolalpha << compare("hello","world") << endl;
    return 0;
}

  模板关键字以template开始,后面跟一个模板参数列表。在调用函数时,编译器会用函数实参来为我们推断模板参数,

生成相应的函数。

2.模板类型参数

  一般的,模板参数列表中有一个类型参数T,在函数内部,我们可以把它看成一个类型说明符,就把它当成一个类类型或

者一个内置类型来使用,模板参数列表中可以使用typenaem ,也可以使用class.

3.非类型模板参数

  除了类型模板参数,我们可以定义非类型模板参数,非类型参数表示一个值而不是一个类型,

例如在C++中,数组名传参时,当我们不想传入数组大小,或者希望能处理不同的数组大小的参

数传入时,使用模板参数。

  

template <size_t N,size_t M>
int fun(const char (&p1)[N],const char (&p2)[M])
{
    return strcmp(p1,p2);
}
int main()
{
    cout << fun("hello","world") << endl;
    return 0;
}

  调用fun()时,会生成 int fun(const char (&p1)[5],const char (&p2)[5]);

  注意:非类型的模板参数必须是常量表达式。

4模板编译

  当编译器发现一个模板时,并不生成代码,只有当我们调用函数时,才生成特定的模板实例,

一般来说,我们在头文件中放置函数的声明,在源文件中进行实现,但是,在有模板的情况下,

为了生成一个实例,编译器需要掌握函数模板的定义,因此,模板的头文件要包含声明和定义。

  模板的编译有两个阶段,一个是检查模板函数的编写和模板函数的调用是否有语法的错误,

第二个阶段发生在模板调用时,编译器生成模板实例,只有在这个阶段才能发生类型相关的错误。

2.类模板

  1.类模板概念

  

template <typename T>
class node{
public:
    T data;
    node<T> *next;
public:
    node(T _data=0)
        : data(_data),next(nullptr){}
};
template<typename T>
class Stack
{
public:
    typedef typename node<T>* ptr;
    Stack()
    {
        head = new node<T>;
        length=0;
    }

    Stack(int num)
    {
        head = new node<T>;
        length=0;
        for(int i=0;i<num;i++)
            insert(0);
    }

    Stack(const Stack& rhs)
    {
        head = new node<T>;
        length=0;
        node<T> *_node = rhs.head;
        for(int i=0;i<rhs.length;i++)
        {
            insert(_node->next->data);
            _node = _node->next;
        }
    }

    Stack &operator=(const Stack& rhs)
    {
        if(this == &rhs) return *this;

        int _length = this->length;
        for(int i=0;i<_length;i++)
        {
            erase();
        }
        head->next = nullptr;
        node<T> *_node = rhs.head;
        for(int i=0;i<rhs.length;i++)
        {
            insert(_node->next->data);
            _node = _node->next;
        }
        return *this;
    }

    virtual ~Stack()
    {
        int _length = length;
        for(int i=0;i<_length;i++)
            erase();
        delete head;
    }
}

  考虑这个语句 typedef typaname node<T>* ptr;

  在实例化之前,编译器不知道T为何物,使用typename 让编译器知道这是一个类型。

  在类模板自己的作用域中,我们可以直接使用模板名而不提供实参。

  在类模板外定义成员时,必须指出返回类型是一个实例化的T。

2.类模板和友元

  在声明友元类时,使用与类模板参数相同的参数声明,友元关系被限定在相同运算符之间

当使用不同的参数声明时,友元类的所有实例都是模板类的友元。

注意:为了引用模板的一个特定实例,我们必须声明模板自身。

3.模板类型别名

  为模板类型定义别名时,由于模板不是类型,我们不能定义一个typedef 引用一个模板,必须使用

它的实例化

  

typedef BST bst;//false
typedef BST<int> bst;//true

4.使用类的类型成员

  当模板参数T为类类型时,使用该类的成员,例如T::value;编译器不知道T是什么

这时,使用typename,告诉编译器这是一个类型。

5.默认模板实参

  我们可以在类模板中为实参类型添加默认类型

  

template <typename T=int>
class BST{};

int main()
{
    BST<> bst;
    return 0;
}

  默认生成int类型的类实例

3.成员模板

  一个类中可以包含本身是模板的成员函数,这种成员函数被称为成员模板。成员模板不能是虚函数。

1.普通类的成员模板

2.类模板的成员模板

  对于类模板,我们也可以定义成员模板,在这种情况下,类和成员模板各有自己独立的模板参数,

template <typename T=int>
class BST{
public:
    template<typename IT> BST(IT a);
};
template<typename T>
template<typename IT>
BST<T>::BST(IT a){

}
int main()
{
    BST<> bst(10);
    return 0;
}

4.重载与模板

  模板的出现很大程度上使得代替了重载的作用。

函数模板可以被另外一个模板或者一个普通非模板函数重载。

template <typename T>
void fun(const T _a)
{
    cout << "T _a" << endl;
    cout << _a << endl;
}
template <typename T>
void fun(T *p)
{
    cout << "T p" << endl;
    cout << p << endl;
}
int main()
{
    const int a=10;
    char *p = "hello,world";
    fun(a);
    fun(p);
    return 0;
}

  即使没有第二个重载函数,字符串依然可以输出,T会成为char*.但是加上重载函数,可以实现精确匹配。

 

    

 

posted @ 2017-04-09 19:52  是召不是昭  阅读(214)  评论(0编辑  收藏  举报