zoukankan      html  css  js  c++  java
  • C++笔记(1) —— 模板

    C++笔记(1) —— 模板

    简述

    面向对象编程(OOP)和泛型编程(GP)是C++语言的两个不同分支,而模板就是C++泛型编程的基础。模板就等于是一个类或者函数的基本公式,在编译时就能获知具体类型从而展开成对应的一份代码。

    模板在什么地方使用,如何使用,想法也很简单。在设计一个类或者函数的时候,如果认为哪一些类型可以抽出来,允许使用者任意指定,那么就可以抽出这些类型,然后将该类型或者函数抽象成一个模板,在使用的时候再去指定类型即可。

    用法

    模板定义可以通过使用

    template <typename T>
    

    这样的形式,声明在类或函数前,然后里面所有用到的类型都用 T 替代即可。

    1. 类模板

    类模板中,一般是将类的数据类型抽出来,在不同的类型下可以同样定义出这个类。例如下面这个二叉搜索树的代码,就是将元素类型抽象成为一个模板 Comparable,在实际使用的时候就可以对应不同的类型生成树。

    template <typename Comparable>
    class BinarySearchTree
    {
    public:
        BinarySearchTree() : root{nullptr} {}
        BinarySearchTree(const BinarySearchTree& rhs) { root = clone(rhs.root); }
        BinarySearchTree(BinarySearchTree&& rhs) : root{ std::move(rhs.root) } { rhs.root = nullptr; }
        ~BinarySearchTree();
    
        BinarySearchTree& operator= (const BinarySearchTree& rhs);
        BinarySearchTree& operator= (BinarySearchTree&& rhs);
    
        const Comparable& findMin() const;
        const Comparable& findMax() const;
        bool contains(const Comparable& x) const { return contains(x, root); }
        bool isEmpty() const { return root == nullptr; }
        void makeEmpty() { makeEmpty(root); }
        void insert(const Comparable& x) { insert(x, root); }
        void insert(Comparable&& x) { insert(std::move(x), root); }
        void remove(const Comparable& x) { remove(x, root); }
    
        void print(ostream& out = cout) const { print(root, out); out << endl;}
    
    private:
        struct BinaryNode
        {
            Comparable element;
            BinaryNode* left;
            BinaryNode* right;
    
            BinaryNode(const Comparable& x, BinaryNode* lt, BinaryNode* rt)
                : element{x}, left{lt}, right{rt} {}
            BinaryNode(Comparable&& x, BinaryNode* lt, BinaryNode* rt)
            : element{std::move(x)}, left{lt}, right{rt} {}
        };
    
        BinaryNode* root;
    }
    

    使用方法也很简单,如下所示,只需要先指定类型然后直接使用即可:

    BinarySearchTree<double> bst;
    bst.insert(3.14);
    

    2. 函数模板

    函数模板和类模板基本也是一样的,同样是将类型抽出来。而使用的时候则更方便,不需要指定类型,编译器会对函数模板进行实参推导。

    template <typename T>
    inline const T& min(const T& a, const T& b)
    {
        return a < b ? a : b;
    }
    
    int r1 = 1, r2 = 2, r3;
    r3 = min(r1, r2);
    

    还有一种比较特别一点的用法,非类型模板函数:

    template<unsigned N, unsigned M>
    int compare(const char (&p1)[N], const char(&p2)[M])
    {
        return strcmp(p1, p2);
    }
    
    compare("hi", "mom");
    

    此时,参数代表了一个值而不是类型,需要通过特定的类型名而不是使用typename或class来指定。

    3. 成员模板

    一个类可以包含本身是模板的成员函数,这个成员就被称为成员模板。

    template <typename T1, typename T2>
    struct pair
    {
        typedef T1 first_type;
        typedef T2 second_type;
    
        T1 first;
        T2 second;
    
        pair() : first(T1()), second(T2()) {}
        pair(const T1& a, const T2& b) : first(a), second(b) {}
    
        template<typename U1, typename U2>
        pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
    }
    

    成员模板的用途一般在一些模板的模拟up-cast上。基类和继承类之间可以有一种up-cast的关系,同样,如果一个模板类可以用任意的T1, T2 构造,那么应该也允许继承自T1,T2的类D1, D2来构造。例如:

    Derived1 dr1;
    Base1 base1(dr1);
    
    Derived2 dr2;
    Base2 base2(dr2);
    
    pair<Derived1, Derived2> p1;
    pair<Base1, Base2> p2(p1);
    

    模板特化

    特化,就是泛化的反面。有时单一的模板可能会无法适合某些实参,这个时候就需要通过特化来满足模板定义这些特定类型。又或者对于某些特定的类型,可以有更好的算法或者更高效的代码,这个时候,也可以通过模板特化来实现。

    如何模板特化也很简单,只需要多加一个特殊版本就可以实现了:

    template <typename T>
    int compare(const T& a, const T& b)
    {
        if (a < b) return -1;
        if (b < a) return 1;
        return 0;
    }
    
    template<>
    int compare(const char* const &p1, const char* const &p2)
    {
        return strcmp(p1, p2);
    }
    
  • 相关阅读:
    Ubuntu 16 安装ElasticSearch
    二叉树
    归并排序
    快速排序
    Git、Github使用
    445. 两数相加 II
    141. 环形链表
    92. 反转链表 II
    19. 删除链表的倒数第N个节点
    2. 两数相加
  • 原文地址:https://www.cnblogs.com/zhqherm/p/12052002.html
Copyright © 2011-2022 走看看