《c++ primer》第16章 模板与泛型编程

泛型编程就是独立于任何特定类型的方式进行编码。模板是泛型编程的基础。使用模板无需了解模板的定义。依赖于编译时的多态性或参数式多态性

模板的定义

  1. 函数模板定义
  2. template <typename T>
    int compare(const T &v1,const T &v2)
    {
          if(v1<v2) return -1;
          if(v2>v1) return 1;
          return 0;
    }

    模板形参表<typename T>
  3. 类模板
    template <class Type>class Queue
    {
     public:
        Queue();
        Type &front();
         ......
    }

  4. 非类型模板形参
    template<class T,size_t N>void array_init(T (&parm)[N])
    {
         for(size_t i = 0;i!= N;++i)
        {
            parm[i] = 0;
        }
    }
    在需要常量表达式的时候,可使用非类型形参指定数组长度
    int x[42];
    array_init(x);   //初始化  array_init(int (&)[42])

  5. 编写模板代码的时候,对实参类型要求尽可能的少是有益的

模板的实例化

  1. 多个类型的实参完全匹配
    template<typename T>
    int compare(const T& v1,const T& v2)
    {
        ......
    }
    int main()
    {
         short si;
         compare(si,1024);//error 应该同为相同的类型
         return 0;
    }

  2. (稍加注意)类型的实参形参受限转换:const转换,数组或函数到指针转换。
    template<T> T fobj(T,T);
    template<T> T fref(const T&,const T&);
    string s1("a value");
    const string s2("another value");
    fobj(s1,s2);   //全部变换为非const类型
    fref(s1,s2);    //转换为const类型
    
    int a[10];
    int b[42];
    fobj(a,b);   //可以将数组名转换为指针
    fref(a,b);   //错误,不能将数组名转换为指针

模板编译类型

  1. 当编译器看到模板的时候,并不立即产生代码,只有用到模板的时候,如调用了函数模板或定义了类模板的对象的时候,编译器才产生特定类型的模板实例。
  2. 区别:调用函数的时候,编译器只需要看到函数的声明。类似,定义类类型的对象,类定义必须可以使用,但成员函数的定义不一定要存在。所以将类定义和函数声明放在头文件中,函数和成员函数的定义放在源文件中。模板不同,要进行实例化,编译器必须能够访问定义模板的源代码
  3. 标准c++为编译模板定义了两种类型。不同之处在于如何使用源文件中的定义。
  4. 包含模型:编译器必须看到所有模板的定义,所以在头文件中添加#include指示。编译效率比较差,会有多个实例化,优化来避免
  5. 分别编译模型:export关键字。指明给定的定义可能需要在其他文件中产生实例化。在一个文件中,一个模板只能定义为导出一次。头文件中类或函数不能声明为export,一般在源文件中的函数定义以及类的定义源文件中声明为export。

类模板成员

  1. 何时实例化类和成员?类模板成员只有为程序所用才进行初始化。如果某函数从未使用过,则不会实例化该成员函数
  2. (有意思)类模板中的友元声明:a将友元关系授予明确的指定的类或函数   b授予对友元所有类型的访问权限   c只授予对类模板或函数模板的特定实例的访问权的友元声明
    //情况b
    template <class Type>class Bar{
       template<class T>friend class Fool;
       template<class T>friend void templ_fcn1(const T&);
    }
    //情况c,只有给定与bar实例有相同模板实参的Foo2和fcn2,才可以为友元
    template <class T>class Foo2;
    template <class T>class templ_fcn2(const T&)
    template <class Type>class Bar{
       friend class Foo2<Type>;
       friend void <Type>templ_fcn2(const Type&);
    }

  3. 成员模板(定义两个模板形参)
    template<class Type>class Queue{
    public:
         template<class It>
         Queue(It beg,It end):head(0),tail(0){       ......}
          ......
    }
    //外部定义的时候,必须含有两个模板形参
    template<class T>template<class Iter>
    void Queue<T>::assign(Iter beg,Iter end)
    {
         destroy();
         copy_elems(beg,end);
    }
  4. static成员,每个实例化都有自己的static成员
    //分享同一个static成员
    Foo<int> fi,fi2,fi3;
    //单独的static成员
    Foo<string>fs

  5. Queue与QueueItem完整定义
    #include <iostream>
    
    // declaration that Queue is a template needed for friend declaration in QueueItem
    template <class Type> class Queue;
    // function template declaration must precede friend declaration in QueueItem
    template <class T> 
    std::ostream& operator<<(std::ostream&, const Queue<T>&);
    
    template <class Type> class QueueItem {
        friend class Queue<Type>;
        // . . .
        // needs access to item and next
        friend std::ostream& 
        operator<< <Type> (std::ostream&, const Queue<Type>&);
        // . . .
    
    // private class: no public section
        QueueItem(const Type &t): item(t), next(0) { }
        Type item;           // value stored in this element
        QueueItem *next;     // pointer to next element in the Queue
    };
    
    
    template <class Type> class Queue {
        // needs access to head 
        friend std::ostream& 
        operator<< <Type> (std::ostream&, const Queue<Type>&);
    public:
        // empty Queue
        Queue(): head(0), tail(0) { }
    public:
        // construct a Queue from a pair of iterators on some sequence
        template <class It> 
        Queue(It beg, It end): 
              head(0), tail(0) { copy_elems(beg, end); }
        // . . .
    
        // copy control to manage pointers to QueueItems in the Queue
        Queue(const Queue &Q): head(0), tail(0) 
                                      { copy_elems(Q); }
        Queue& operator=(const Queue&);
        ~Queue() { destroy(); }
    
        // replace current Queue by contents delimited by a pair of iterators
        template <class Iter> void assign(Iter, Iter);
        // rest of Queue class as before
    
        // return element from head of Queue
        // unchecked operation: front on an empty Queue is undefined
        Type& front()             { return head->item; }
        const Type &front() const { return head->item; }
    
        void push(const Type &);      // add element to back of Queue
        void pop();                   // remove element from head of Queue
    
        bool empty() const {          // true if no elements in the Queue
            return head == 0;
        }
    private:
        QueueItem<Type> *head;         // pointer to first element in Queue
        QueueItem<Type> *tail;         // pointer to last element in Queue
    
        // utility functions used by copy constructor, assignment, and destructor
        void destroy();                // delete all the elements
        void copy_elems(const Queue&); // copy elements from parameter
    private:
        // version of copy to be used by assign to copy elements from iterator range
        template <class Iter> void copy_elems(Iter, Iter); 
    };
    需要注意的是:输出函数不是成员函数,而是两个类的友元;  QueueItem中声明了一对一的友元Queue;  Queue中有成员模板的定义
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值