C++类模板

作者: 仰简 | 来源:发表于2023-01-07 22:46 被阅读0次

一、定义 C++ 类模板

template<typename T>
class Stack {
private:
    std::vector<T> elements;
public:
    void push(T const& ele);
    void pop();
    T const& top() const;
    bool empty() const {
        return elements.empty();
    }
};

template<typename T>
void Stack<T>::push(const T &ele) {
    elements.push_back(ele);
}

template<typename T>
void Stack<T>::pop() {
    assert(!elements.empty());
    elements.pop_back();
}

template<typename T>
T const & Stack<T>::top() const {
    assert(!elements.empty());
    return elements.back();
}

定义一个 C++ 类模板和定义一个函数模板类似,可以指定一个或者多个模板参数标识符。
在类外定义成员函数的实现时,需要带上模板参数标识符。

template<typename T> xxx Stack<T>::yyy() {}

定义构造函数、拷贝构造函数以及拷贝赋值函数时,不需要指定模板参数标识符。但是你也可以指定参数标识符,只不过效果上是等价的,看起来还显得冗余。

    Stack(){}
    Stack(Stack const& s) {
        this->elements = s.elements;
    }
    Stack& operator=(Stack const & s) {
        this->elements = s.elements;
        return *this;
    }

总的来说,基本原则就是,在只需要类名字而不需要参数类型时可以不指定模板参数标识符,需要参数类型时则必须带上模板参数标识符。
另外,与非模板类不同的是,模板不能定义在函数或者局部分作用域 {} 的内部,只能定义在全局 / 命名空间内。

二、模板类的使用

    Stack<int> intStack;
    Stack<float> floatStack;
    Stack<char> charStack;
    Stack<int *> intPointerStack;
    Stack<Stack<int>> intStackStack;

定义好了模板之后,就可以把模板当作一个普通的类来使用了,比如可以用来定义变量。定义变量时,可以指定类型参数为基本的类型、类类型以及模板类型。模板参数还可以是指针,但不能是引用。为引用时,会得到下面的错误。

image.png

模板类型作为参数时,在 c++11 之前右边的尖括号必须有空格,否则 >> 会被语义解析为右移运算符。

Stack<Stack<int> > intStackStack;

除了用来定义变量,模板类还可以被当作函数参数,可传值、传引用 & 传指针。函数参数可被 const 以及 volatile 修饰。

总的来说,基本原则是,任意类型都可以作为模板参数。但对于模板如果有运算符的操作,那模板参数的实参类型也必须支持其所有的运算符操作。

三、模板类重载运算符

下面是一个重载输出的运算符实现,和非模板类一样,建议将运算法重载定义为友元函数。

  void printOn(std::ostream& strm) {
        for (T const & elem : elements) {
            strm << elem << ' ';
        }
    }

    friend std::ostream & operator<< (std::ostream& strm, Stack<T> const& s) {
        s.printOn(strm);
        return strm;
    }

四、特化

特化是指对模板的参数定义一个特定类型的实现,对于被特化的模板,其成员变量和成员函数也都定义为特化实现。

template<>
class Stack<std::string> {
private:
    std::vector<std::string> elements;
public:
    void push(std::string const& ele);
    std::string const& top() const;
    void pop();
    bool empty() const {
        return elements.empty();
    }
    void printOn(std::ostream& strm) const{
        for (std::string const & elem : elements) {
            strm << elem << ' ';
        }
    }

    friend std::ostream & operator<< (std::ostream& strm, const Stack<std::string> &s) {
        s.printOn(strm);
        return strm;
    }
};

void Stack<std::string>::push(const std::string &ele) {
    elements.push_back(ele);
}

std::string const& Stack<std::string>::top() const {
    assert(!elements.empty());
    return elements.back();
}

void Stack<std::string>::pop() {
    assert(!elements.empty());
    elements.pop_back();
}

使用特化类

    // 将使用特化版本
    Stack<std::string> stringStack;
    stringStack.push("aaaa");
    stringStack.push("bbbb");
    std::cout << stringStack << std::endl;

template<> class Stack<std::string> {}; 可以看成全特化,虽然其只有一个模板参数。相对应的还有偏特化或者叫部分特化。
偏特化有两种情况,一种是模板参数仍然是存在的,而针对模板参数的指针做特化,比如

template<typename T>  class Stack<T*> {};

另一种情况是多个模板参数的情况下,只特化其中一部分参数,比如

// 有如下原始模板类
template<typename T, typename U> class Stack {};
// 那么可以得到偏特化的模板类
template<typename T> class Stack<T,T> {};
template<typename T> class Stack<T, int> {};
template<typename T, typename U> class Stack<T* , U*> {};

五、类模板的默认参数与类模板别名

类模板的参数与函数模板一样,也可以指定默认参数。

template<typename T, typename Cont = std::vector<T>> class Queue {
private:
    Cont elements;
public:
    void push(T const& ele);
    void pop();
    T const& top() const;
};

类模板的别名是为了让模板使用起来更简单,可以通过 typedef 来定义,c++11 以后也可以通过 using 来定义。

typedef Stack<int> IntStack;
using LongStack = Stack<long>;

上面的两者是等价的,但 using 还可以用来定义别名模板,typedef 是不可以的。比如,下面的定义是合法的。

template<typename T>
using QueueStack = Queue<T, std::queue<T>>;
// 使用 QueueStack
QueueStack<int> intQueueStack;

但如果你用 typedef 来定义的话,就会得到如下的错误。

image.png

使用 using 和 typename 还可以定义类的成员别名。

template<typename T, typename Cont = std::vector<T>> class Queue {
private:
    Cont elements;
public:
    void push(T const& ele);
    void pop();
    T const& top() const;
public:
    using Iterator = int;
    // 可以这样定义一个成员别名模板
    using MyIterator = typename Queue<T>::Iterator;
};
// 然后这样使用 MyIterator
Queue<int>::MyIterator it;
// 还可以这样定义成员别名模板
template<typename T>
using MyOuterIterator = typename Queue<T>::Iterator;
// 然后这样使用
MyOuterIterator<int> outer_it;

六、类模板的类型推导

从 c++17 开始允许类模板的类型推导,实现方式比如,可以通过实现一个带参的构造函数来推导。

explicit Stack(T &ele) : elements({ele}) {};

相关文章

  • Geekband C++ 第五周

    概述 C++模板简介 函数模板 C++类模板 操作符重载 泛型编程 容器

  • C++类模板

    一、定义 C++ 类模板 定义一个 C++ 类模板和定义一个函数模板类似,可以指定一个或者多个模板参数标识符。在类...

  • 10-C++远征之模板篇-学习笔记

    C++远征之模板篇 将会学到的内容: 模板函数 & 模板类 -> 标准模板类 友元函数 & 友元类 静态数据成员 ...

  • 慕课网-C++远征之模板篇(上)-学习笔记

    C++远征之模板篇 将会学到的内容: 模板函数 & 模板类 -> 标准模板类 友元函数 & 友元类 静态数据成员 ...

  • C++ STL初识及整理

    概述 简介 简单介绍:C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些...

  • C++ 模板开发

    C++模板开发分为两类: 模板函数开发 模板类开发 模板函数语法: template是关键字,

  • C++11泛型 - 类模板

    前面我们介绍了函数模板。今天我们来看看C++的另一种泛型:类模板。C++中类模板通常是容器(如std::vecto...

  • 【C++ Templates(11)】深入模板基础

    参数化声明 C++现在支持四种基本模板:类模板,函数模板,变量模板(variable template),别名模板...

  • 第三章 摘录

    3.3 标准库类型vector c++既有类模板,也有函数模板,其中vector是一个类模板。表示对象的集合,其中...

  • C++基础一文通(五)泛型 / 模板

    C++另一种编程思想称为 ==泛型编程== ,主要利用的技术就是模板 C++提供两种模板机制:函数模板和类模板 一...

网友评论

    本文标题:C++类模板

    本文链接:https://www.haomeiwen.com/subject/yegecdtx.html