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);
}
原文地址:https://www.cnblogs.com/zhqherm/p/12052002.html