c++模板(二)---------类模板

本文深入讲解C++中的类模板概念,包括定义、非类型参数、多功能性等方面,并介绍模板的具体化方式及其应用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二、类模板
1、定义类模板
示例,一个基于模板自定义的栈:

#ifndef STACK_H_
#define STACK_H_

/*一个基于模板自定义的栈*/
template <class Type>
class Stack
{
private:
    enum { MAX = 10};//一个常量用来辅助数组定义
    Type items[MAX]; //内部数组存放着 type们
    int top;
public:
    Stack();
    bool isempty();
    bool isfull();
    bool push(const Type & item);
    bool pop(Type & item);
    ~Stack();
};

template <class Type>
Stack<Type>::Stack()
{
    top = 0;
}

template <class Type>
bool Stack<Type>::isempty() {
    return top == 0;
}

template <class Type>
bool Stack<Type>::isfull() {
    return top == MAX;
}

template <class Type>
bool Stack<Type>::push(const Type & item) {
    if (top < MAX) {
        items[top++] = item;
        return true;
    }
    else {
        return false;
    }
}

template <class Type>
bool Stack<Type>::pop(Type & item) {
    if (top > 0) {
        item = items[--top];
        return true;
    }
    else {
        return false;
    }
}

template <class Type>
Stack<Type>::~Stack()
{
}
#endif // !STACK_H_

需注意:成员函数使用Stack< Type >进行限定,而不是Stack。
附上简单的测试代码:

#include "stdafx.h"
#include <iostream>
#include "Stack.h"
#include "Stack.cpp"
int main() {
    using namespace std;
    Stack<int> mStack;
    int i = 0;
    for (; ;i++)
    {
        if (mStack.isfull())break;
        mStack.push(i);
    }
    int result;
    for (; i > 0; i--) {
        mStack.pop(result);
        cout << "out: " << result << endl;
    }
    system("pause");
}
执行结果:
out: 9
out: 8
out: 7
out: 6
out: 5
out: 4
out: 3
out: 2
out: 1
out: 0
请按任意键继续. . .

2、非类型参数
有模板头: template < classs T ,int n > 的话。关键字class (或与它等价的typename)指出了 T 为类型参数,int 指出了n的类型为int。这种参数(指定特殊的类型而不是用作泛型名)成为非类型(non-type)或表达式(expression)参数。
假设有下面的声明:
ArrayTP< double , 12 > eggweights。这将导致编译器定义名为ArrayTP< double,12>的类 ,并创建一个类型为ArrayTP< double ,12>的eggweight对象,定义类时,将使用double替换T,使用12替换n 。
表达式是有一些限制的,表达式参数可以是整型、枚举、引用或指针。
3、模板的多功能性
模板可以用作基类,也可以用作组件类,还可以用作其他模板的类型参数。
a、递归使用模板
示例:
ArrayTP< ArrayTP< int > > aas;
b、使用多个类型参数
示例:

template <class T1,class T2> 
class Test {
private:
     T1 t1;
     T2 t2;

public :
        ....
};

c、默认类型模板参数
模板的另一项新特性是,可以为类型参数提供默认值;

template <class T1, class T2 = int> class Topo {...};

这样如果省略T2时,编译器就使用int .
注意:虽然可以为类模板类型参数提供默认值,但不能为函数模板提供默认值。然而可以为非类型参数提供默认值,这对于类模板和函数模板都很适合。
4、模板具体化
假设有类定义如下:

template <class T1, int  n = int>
class Test {
private:
    T1 t1;

public:
    ....
};

A. 隐式实例化

Test< double, 30>  test;

这也是最常用的实例化。
注意编译器在需要对象之前,不会生成类的隐式实例。

Test< double, 21> * pt; // 一个指针,还没有对象
    pt = new Test< double, 21 >(); //有了一个对象

B. 显示实例化

template class Test< int, 100>; //生成 Test<int ,100> class ;

在这种情况下,虽然没有创建或提及类对象,编译器也将生成类声明(包括方法定义)。和隐式实例化一样,也将根据通用模板来生成具体化。

C. 显示具体化

template<> class Test <int, 100> { //显示具体化
....
}

D. 部分具体化
假设有类:

template <class T1,class T2>
class Test2 {
    .....
};
template <class T1> class Test2<T1, int> {...};

则上述声明将T2具体化为int ,但T1保持不变,如果指定所有的类型,则<>内为空。这将导致显示具体化。
如果有多个模板可供选择,编译器会使用具体化程度最高的模板。
5、成员模板
示例:

#ifndef BETA_H_
#define BETA_H_
#include <iostream>
using namespace std;
template <typename T>
class beta
{
private:
    template < typename V >
    class hold {
    private :
        V val;
    public:
        hold(V v = 0) : val(v) {}
        void show() const { cout << val << endl; }
        V value() const { return val; }
    };
    hold<T> q;
    hold<int> n;

public:
    beta(T t, int i): q(t), n(i) {};
    template <typename U >
    U blab(U u, T t) {
        return (n.value() + q.value()) * u / t;
    }
    void show() const {
        q.show();
        n.show();
    }
};

#endif // !BETA_H_

如果编译器接收模板成员在外面定义的话,也可以使用如下代码
beta.h

#ifndef BETA_H_
#define BETA_H_
#include <iostream>
using namespace std;
template <typename T>
class beta
{
private:
    template < typename V >
    class hold;
    hold<T> q;
    hold<int> n;
public:
    beta(T t, int i): q(t), n(i) {};
    template <typename U >
    U blab(U u, T t);
    void show() const {
        q.show();
        n.show();
    }
};

#endif // !BETA_H_

beta.cpp

#include "stdafx.h"
#include "beta.h"


template<typename T >
template <typename V>
class beta<T>:: hold {
private:
    V val;
public:
    hold(V v = 0) : val(v) {}
    void show() const { cout << val << endl; }
    V value() const { return val; }
};


template <typename T>
template <typename U >
U beta<T>:: blab(U u, T t) {
    return (n.value() + q.value()) * u / t;
}

因为模板是嵌套的,因此必须使用template< typename T> template< typename V >
而不能使用template< typename T ,typename V >
6、将模板用作参数
示例:

template < template <typename T > class  Thing> 
    class Crab {
        private:
            Thing< int > s1;
            Thing < double > s2 ;
            ...
        public :
        ....
    };

其中 template < typename T > class 是类型,Thing 是参数。Thing必须一个模板类。假如实例化时使用 Crab< Stack > nebula; 那么 Crab中的那2个成员变量将会被替换为 Stack< int > s1 和 Stack< double > s2 ;
还可以混合使用模板参数与常规参数例如:

template < template <typename T > class  Thing  ,typename U, typename V> 
    class Crab {
    private :
        Thing<U > s1;
        Thing<V> s2;
    };

7、模板类和友元
模板的友元分3类

  • 非模板友元
  • 约束模板友元
  • 非约束模板友元

    A、非模板友元:

#include<iostream>
using namespace std;

template <class T>
class HasFriend
{
private :
    T item;
    static int ct;
public:
    friend void counts();
    friend void reports(HasFriend<T> &);
    HasFriend(const T & i) item(i) {ct++};
    ~HasFriend() {ct --};
};

template <typename T>
int HasFriend<T>::ct = 0;

//非模板友元
void  counts(){

}

//非模板友元 具体化接收 HasFriend<int> 参数
void reports(HasFriend<int > & a) {

}
//非模板友元 具体化接收 HasFriend<double> 参数
void reports(HasFriend<double> & b) {

}

B、约束模板友元

#include<iostream>
using namespace std;

template <typename T > void counts();
template <typename T > void report(T &);

template <typename T>
class HasFriend
{
private :
    T item;
    static int ct;
public:
    friend void counts<T>();
    /*这里可以使用<> 因为可以从形参中推断出类型 */
    friend void report<>(HasFriend<T> &);
    HasFriend(const T & i) item(i) {ct++};
    ~HasFriend() {ct --};
};

template <typename T>
int HasFriend<T>::ct = 0;

template <typename T >
void  counts(){

}
template <typename T>
void report(T & hf){

}

使用约束模板友元主要分三步
1.在类定义之前声明每个模板函数
2.在类中再次将模板函数声明为友元,这些语句根据模板参数的类型声明具体化
3.定义函数

C、非约束模板友元
通过类内部声明模板,可以创建非约束友元函数。

template <typename T >
class ManyFriend {

    ....
        template <typename C, typename D> friend void show(C &, D &);
};

附上测试代码:

#include<iostream>
using namespace std;

template <typename T > void counts();
template <typename T > void report(T &);

template <typename T>
class HasFriend
{
private :
    T item;
    static int ct;
public:
    friend void counts<T>();
    /*这里可以使用<> 因为可以从形参中推断出类型 */
    friend void report<>(T &);

    template <typename C, typename D> friend void show(C & , D &);
    HasFriend(const T & i) :item(i) { ct++; }
    ~HasFriend(){ ct--; }
};

template <typename T>
int HasFriend<T>::ct = 0;

template <typename T >
void  counts(){

}
template <typename T>
void report(T & hf){

}

template <typename C, typename D>  void show(C & c,  D & d) {
    cout << "c : " << c << " d: " << d << endl;
}
#include "stdafx.h"
#include <iostream>
#include "HasFriend.h"


int main() {
    HasFriend<int>  h();
    int a = 10;
    double b = 20.1;
    show(a, b);
    system("pause");
}

执行结果:
c : 10 d: 20.1
请按任意键继续. . .

8、模板别名
template < typename T >
using arrtype = std::array< T ,12> ;
这将arrtype定义为了一个模板别名,可以使用它来指定类型。如:
arrtype< double > gallons; //等价于 std::array< double ,12>
c++11 还允许将语法using= 用于非模板。用于非模板时,这种语法与常规语法typedef等价。
typedef const char * pc1 ;
using pc2 = const char * ;
这2种等价。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值