C++ 类模板

C++ 类模板

简介

本文中的内容主要参考C++ Primer Plus(第6版)中文版一书第14章中的内容。该篇博客主要根据自己使用模板的经验,结合书中的内容,总结类模板的原理和使用方法。

定义类模板

C++类模板为生成通用的类生命提供了一种更好的方法。模板提供参数化类型,即能够将类型名作为参数传递给接收方来建立类或函数。类模板以template <typename Type>开头,关键字template告诉编译器将要定义一个模板。
类模板的定义如下代码所示:

template <typename Type>
class Stack
{
private:
    enum {MAX = 10};    // constant specific to class
    Type items[MAX];    // holds stack items
    int top;            // index for top stack item
public:
    Stack();
    bool isempty();
    bool isfull();
    bool push(const Type & item); // add item to stack
	bool pop(Type & item)
	{
	    if (top > 0)
	    {
	        item = items[--top];
	        return true;
	    }
	    else
	        return false; 
	}
	
	Stack& operate=(const Stack& st);
};

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>
Stack<Type> & Stack<Type>::operator=(const Stack<Type> & st)
{

}

上述代码中列出了类模板和成员函数模板,这些模板不是类和成员函数的定义,它们只是C++编译器指令,说明了如何生成类和成员函数定义,因此不能将模板成员函数放在独立的实现文件中,必须与特定的模板实例化请求一起使用,最简单的方法是将所有模板信息放在一个头文件中,并在要使用这些模板的文件中包含该头文件。
注意:原型将赋值运算符函数的返回类型声明为Stack引用,而实际的模板函数定义将类型定义为Stack<Type>。前者是后者的缩写,缩写只能够在类中使用,但在类的外面,必须要指定返回类型以及使用作用域解析运算符时,必须使用完整的Stack<Type>

使用模板类

仅在程序中包含模板并不能生成模板类,而必须请求实例化。

Stack<int> kernels;
Stack<string> colones;

上述代码中分别定义了两个不同类型的模板类。但需要注意的是与定义模板函数不同的是,定义模板类必须显示地提供所需的类型。因为函数模板可以根据参数的类型来确定要生成哪种函数,如下代码所示:

template <typename T>
void simple(T t) { cout << t << '\n'; }

simple(2);        // generate void simple(int)
simple("two")     // generate void simple(const char*)

数组模板示例和非类型参数

模板常用作容器类,下面将通过一个例子来深入探讨模板设计和使用的其他几个方面。

template <class T, int n>
class ArrayTP
{
private:
    T ar[n];
public:
    ArrayTP() {};
    explicit ArrayTP(const T & v);
    virtual T & operator[](int i);
    virtual T operator[](int i) const;
};

template <class T, int n>
ArrayTP<T,n>::ArrayTP(const T & v)
{
    for (int i = 0; i < n; i++)
        ar[i] = v;
}

template <class T, int n>
T & ArrayTP<T,n>::operator[](int i)
{
    if (i < 0 || i >= n)
    {
        std::cerr << "Error in array limits: " << i
            << " is out of range\n";
        std::exit(EXIT_FAILURE);
    }
    return ar[i];
}

template <class T, int n>
T ArrayTP<T,n>::operator[](int i) const
{
    if (i < 0 || i >= n)
    {
        std::cerr << "Error in array limits: " << i
            << " is out of range\n";
        std::exit(EXIT_FAILURE);
    }
    return ar[i]; 
}

上述代码中关键字class指出T为类型参数,int指出n的类型为int,这种参数称为非类型(non-type)或表达式(expression)参数。声明形式如下:

ArrayTP<double, 12> eggweights;

注意:表达式参数有一些限制,表达式参数可以是整型、枚举、引用或还真,因此,double m是不合法的,但double *rmdouble *pm是合法的。另外模板代码不能修改参数的值,也不能使用参数的地址。另外,实例化模板时,用作表达式参数的值必须是常量表达式。

模板多功能性

  • 递归使用模板
    ArrayTP< ArrayTP<int,5>, 10> twodee;
    该定义等价于常规数组声明:int twodee[10][5]
    
    注意:在模板语法中,维的顺序与等价的二维数组相反。
  • 使用多个类型参数
    模板可以包含多个类型参数,下面代码中就使用了两种类型参数。
    template <class T1, class T2>
    class Pair
    {
    }
    
  • 默认类型模板参数
    类模板的另一新特性是可以为类型参数提供默认值:
    template <class T1, class T2 = int> class Topo { ... };  
    
    虽然可以为类模板类型参数提供默认值,但不能为函数模板参数提供默认值。然后可以为非类型参数提供默认值,这对于类模板和函数模板都是适用的。

模板的具体化

类模板与函数模板很相似,可以有隐式实例化、显示实例化和显示具体化。

  • 隐式实例化
    目前为止,上述代码都是适用的隐式实例化(implicit instantiation)。即它们声明一个或多个对象,指出所需的类型,而编译器使用通用模板生成具体的类定义:

    ArrayTP<int, 100> stuff; // implicit instantiation
    

    编译器在需要对象之前,不会生成类的隐式实例化:

    ArrayTP<double, 30>* stuff; // a pointer, no object needed yet
    stuff = new ArrayTP<double, 30>; // noew an object is needed
    
  • 显示实例化

    template class ArrayTP<int, 100> ; // explicit instantiation
    

    在这种情况下,虽然没有创建或提及类对象,编译器也将生成类声明(包括方法定义)。

  • 显示具体化

    template <> class Classname<specialized-type-name> { ... }; // explicit specialized
    
  • 部分具体化
    C++还允许部分具体化(partial specialization),即部分限制模板的通用性。例如,部分具体化可以给类型参数之一指定具体的类型:

    template<typename T1> class Pair<T1, int> { ... };
    

    也可以通过为指针提供特殊版本来部分具体化现有的模板:

    template<class T>  // general version
    class Feeb { ... }
    template<class T*>  // pointer partial specialization version
    class Feeb { ... }
    
    Feeb<char> fb1;   // use general Feeb template
    Feeb<char *> fb1; // use Feeb T* specialization
    

    部分具体化特性使得能够设置各种限制。

    // generate template
    template <class T1, class T2, class T3> class Trio { ... };
    // specilization with T3 set to T2
    template <class T1, class T2, class T3> class Trio<T1, T2, T2> { ... };
    // specilization with T3 and T2 set to T1*
    template <class T1> class Trio<T1, T1*, T1*> { ... };
    

    成员模板

    模板可用作结构、类或模板类的成员。如下述代码所示:

    template <typename T>
    class beta
    {
    private:
        template <typename V>  // nested template class member
        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;             // template object
        hold<int> n;           // template object
    public:
        beta( T t, int i) : q(t), n(i) {}
        template<typename U>   // template method
        U blab(U u, T t) { return (n.Value() + q.Value()) * u / t; }
        void Show() const { q.show(); n.show();}
    };
    

    将模板用作参数

    STL的实现就比较多的使用该功能。如下述代码所示,<typename T>表示一种模板类型。

    template <template <typename T> class Thing>
    class Crab
    {
    private:
        Thing<int> s1;
        Thing<double> s2;
    public:
        Crab() {};
        // assumes the thing class has push() and pop() members
        bool push(int a, double x) { return s1.push(a) && s2.push(x); }
        bool pop(int & a, double & x){ return s1.pop(a) && s2.pop(x); }
    };
    

    模板类和友元

    模板的友元分3类:

    • 非模板友元
    • 约束模板友元,即友元的类型取决于类被实例化时的类型
    • 非约束模板友元,即友元的所有具体化都是类的每一个具体化的友元

    1. 模板类中的非模板友元函数

      template <typename T>
       class HasFriend
       {
       private:
           T item;
       public:
           HasFriend(const T & i) : item(i) {ct++;}
           ~HasFriend()  {ct--; }
           friend void counts();
           friend void reports(HasFriend<T> &); // template parameter
       };
       // non-template friend to all HasFriend<T> classes
       void counts()
       {
           cout << "int count: " << HasFriend<int>::ct << "; ";
           cout << "double count: " << HasFriend<double>::ct << endl;
       }
       
       // non-template friend to the HasFriend<int> class
       void reports(HasFriend<int> & hf)
       {
           cout <<"HasFriend<int>: " << hf.item << endl;
       }
       
       // non-template friend to the HasFriend<double> class
       void reports(HasFriend<double> & hf)
       {
           cout <<"HasFriend<double>: " << hf.item << endl;
       }
      
    2. 模板类的约束模板友元函数

      // template prototypes
      template <typename T> void counts();
      template <typename T> void report(T &);
      
      // template class
      template <typename TT>
      class HasFriendT
      {
      private:
         TT item;
         static int ct;
      public:
         HasFriendT(const TT & i) : item(i) {ct++;}
         ~HasFriendT() { ct--; }
         friend void counts<TT>();
         friend void report<>(HasFriendT<TT> &);
      };
      

      注意:从上述代码可以看出,需要在类定义的前面声明每个模板函数;另外由于counts函数没有用来推断类型的具体化的函数参数,因此在使用的时候需要使用如下形式:

      counts<int>();
      
    3. 模板类的非约束模板友元函数

      template <typename T>
      class ManyFriend
      {
      private:
         T item;
      public:
         ManyFriend(const T & i) : item(i) {}
         template <typename C, typename D> friend void show2(C &, D &);
      };
      
      template <typename C, typename D> void show2(C & c, D & d)
      {
         cout << c.item << ", " << d.item << endl;
      }
      

    模板别名(C++11)

    template<typename T> using arrtype = std::array<T, 12>;
    

    这将arrtype定义为一个模板别名,可使用它来指定类型,如下代码所示:

    arrtype<double> gallons;
    arrtype<int> days;
    arrtype<std::string> months;
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值