当前位置: 首页 > article >正文

C++ 类模板教程

C++ 的类模板是泛型编程的核心特性之一, 它让我们能够编写适用于多种类型的通用代码, 从而提高代码的复用性和扩展性.
本教程通过栈的实现为例, 深入探讨类模板的实现, 使用, 以及特化, 偏特化, 默认参数和类型别名等高级特性, 帮助您更全面地掌握这一强大工具.

1. 实现一个 Stack

笔者选择用一个栈的模板来做演示, 因为栈的接口和功能相对而言比较简单. Stack模板支持如下操作:

  1. Push: 入栈一个元素
  2. Pop: 弹出一个元素
  3. Top: 获取栈顶的元素
  4. Empty: 栈是否为空
  5. Size: 栈的元素数量

实现代码array.hpp

template <typename T>
class Stack {
 private:
  T* data_ = nullptr;
  size_t capacity_ = 10; // 原始的空间为10
  size_t length_ = 0;

  void resize(size_t new_capacity) {
    T* new_data = new T[new_capacity];
    for (size_t i = 0; i < length_; ++i) {
      new_data[i] = data_[i];
    }
    delete[] data_;
    data_ = new_data;
    capacity_ = new_capacity;
  }

 public:
  Stack() { data_ = new T[capacity_]; }

  ~Stack() { delete[] data_; }

  void Push(const T& value) {
    if (length_ == capacity_) {
      resize(capacity_ * 2);
    }
    data_[length_++] = value;
  }

  void Pop() {
    if (length_ == 0) {
      throw std::out_of_range("Index out of range");
    }
    --length_;
  }

  [[nodiscard]] size_t Size() const { return length_; }

  [[nodiscard]] bool Empty() const { return length_ == 0; }

  T Top() const {
    if (0 == length_) {
      throw std::out_of_range("Index out of range");
    }
    return data_[length_ - 1];
  }
};

从上面的代码可以看到

  1. 模板类相比普通类多了一个泛型声明: template <typename T>
  2. 在模板类的内部, 数据成员或者函数成员都可用使用泛型T
  3. 类模板的实现通常是在一个头文件中, 不需要像普通类进行头文件/源文件分离.

2. 如何使用类模板

下面是一个简单的例子, 演示如何使用 Stack 类模板. 用法跟 STL 库中的容器类使用非常相似, 类似std::vector.

Stack<int> arr;

arr.Push(10);
arr.Push(20);
arr.Push(30);

std::cout << "Stack size: " << arr.Size() << std::endl;
std::cout << "Stack top: " << arr.Top() << std::endl;

arr.Pop();
std::cout << "After Pop, size: " << arr.Size() << std::endl;
std::cout << "Stack top: " << arr.Top() << std::endl;

3. 类模板的特化

类模板的特化可以为特定类型提供专门的实现.
例如, 为 bool 类型特化 Stack.
用一个 bit表示 bool 值, 这样就可以节省很多存储空间.

template <>
class Stack<bool> {
 private:
  uint8_t* data_ = nullptr;
  size_t capacity_ = 10;
  size_t length_ = 0;

  void resize(size_t new_capacity) {
    auto* new_data = new uint8_t[new_capacity];
    for (size_t i = 0; i < length_; ++i) {
      new_data[i] = data_[i];
    }
    delete[] data_;
    data_ = new_data;
    capacity_ = new_capacity;
  }

 public:
  Stack() { data_ = new uint8_t[(capacity_ + 7) / 8]; }
  ~Stack() { delete[] data_; }

  [[nodiscard]] size_t Size() const { return length_; }
  [[nodiscard]] bool Empty() const { return length_ == 0; }

  void Push(bool value) {
    if (length_ == capacity_) {
      resize(capacity_ * 2);
    }
    size_t byte_index = length_ / 8;
    size_t bit_index = length_ % 8;
    if (value) {
      data_[byte_index] |= (1 << bit_index);
    } else {
      data_[byte_index] &= ~(1 << bit_index);
    }
    ++length_;
  }

  void Pop() {
    if (0 == length_) {
      throw std::out_of_range("Index out of range");
    }
    --length_;
  }

  [[nodiscard]] bool Top() const {
    if (0 >= length_) {
      throw std::out_of_range("Index out of range");
    }
    size_t byte_index = (length_ - 1) / 8;
    size_t bit_index = (length_ - 1) % 8;
    uint8_t result = (data_[byte_index] >> bit_index) & 1;
    return result == 1;
  }
};

4. 类模板的偏特化

偏特化允许对部分模板参数提供专门实现. 例如, Stack 类针对指针类型的偏特化.

注意: 这个偏特化的实现是假定了Stack对与入栈的指针具有所有权, Stack在析构的时候会释放栈内的指针.

template <typename T>
class Stack<T*> {
 private:
  T** data_ = nullptr;
  size_t capacity_ = 10;
  size_t length_ = 0;

  void resize(size_t new_capacity) {
    auto* new_data = new T*[new_capacity];
    for (size_t i = 0; i < length_; ++i) {
      new_data[i] = data_[i];
    }
    delete[] data_;
    data_ = new_data;
    capacity_ = new_capacity;
  }

 public:
  Stack() { data_ = new T*[capacity_]; }

  ~Stack() {
    for (size_t i = 0; i < length_; ++i) {
      delete data_[i];
    }
    delete[] data_;
  }
  void Push(T* value) {
    if (length_ == capacity_) {
      resize(capacity_ * 2);
    }
    data_[length_++] = value;
  }
  void Pop() { length_--; }
  T* Top() { return data_[length_ - 1]; }
  [[nodiscard]] size_t Size() const { return length_; }
  [[nodiscard]] bool Empty() const { return length_ == 0; }
};

对比总结

特性特化偏特化
作用范围单一具体类型(如 bool)一类类型(如 T*, std::vector<T>)
实现独立性独立于通用模板通常继承通用模板的逻辑
使用复杂性较简单较复杂, 需要匹配模板参数模式
应用场景对单一类型需求的特殊优化针对一类类型需求的共性优化

5. 类模板默认参数

观察上面的代码, 我们会发现有个问题是默认的大小是写死的. 这个多少有些不灵活, 我们可以增加一个模板参数, 来决定栈预留空间.

// 模板类 Array 的一般实现
template <typename T, size_t N = 10>
class Stack {
 private:
  T* data_ = nullptr;
  size_t capacity_ = N;
  size_t length_ = 0;

  // 其他代码不变
};

6. 类模板 type alias

通过 using 创建模板类型别名:

#include "stack.hpp"
using IntStack = Stack<int>;

int main() {
  IntStack stack;
  stack.Push(1);
}

总结

本教程从基础到高级应用, 详细讲解了 C++ 类模板的实现与特性. 希望这些内容能帮助您更好地理解并应用类模板.

源码链接

源码链接


http://www.kler.cn/a/504776.html

相关文章:

  • 汽车免拆诊断案例 | 2007 款法拉利 599 GTB 车发动机故障灯异常点亮
  • 华为数通HCIE备考经验分享
  • 爬虫请求失败时如何处理?
  • 如何在Jupyter中快速切换Anaconda里不同的虚拟环境
  • 穷举vs暴搜vs深搜vs回溯vs剪枝系列一>优美的排列
  • 【优选算法篇】:分而治之--揭秘分治算法的魅力与实战应用
  • 分布式ID的实现方案
  • Pacs系统开发之Dcm4chee代码结构分析
  • 搭建 RUST 交叉编译环境
  • 建筑综合布线可视化管理
  • 大模型微调介绍-Prompt-Tuning
  • WPS excel使用宏编辑器合并 Sheet工作表
  • 苍穹外卖(七) 缓存商品、购物车
  • 【React】新建React项目
  • Flume【部署 01】CentOS Linux release 7.5 安装配置 apache-flume-1.9.0 并验证
  • 在AI智能中有几种重要的神经网络类型?6种重要的神经网络类型分享!
  • 阀井可燃气体监测仪,开启地下管网安全新篇章-旭华智能
  • Linux 机器学习
  • YunSDR通信小课堂-33
  • 数据仓库的复用性:统一数据标准和规范
  • 康谋产品 | 深度自然匿名化:隐私保护与视觉完整性并存的未来!
  • 【PowerQuery专栏】PowerQuery中的函数与Excel和Python的异同
  • 模拟器多开窗口单IP与代理IP关系
  • docker实际应用记录
  • Transformers Learning Rate Schedules
  • 微软震撼发布:Phi-4语言模型登陆Hugging Face