首页 > 编程语言 >C++ 类模板教程

C++ 类模板教程

时间:2025-01-15 21:30:24浏览次数:3  
标签:教程 capacity C++ length new data 模板 size

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++ 类模板的实现与特性. 希望这些内容能帮助您更好地理解并应用类模板.

源码链接

源码链接

标签:教程,capacity,C++,length,new,data,模板,size
From: https://blog.csdn.net/arong_xu/article/details/145168432

相关文章

  • 倍增算法【模板】
    原题链接https://www.luogu.com.cn/problem/P3865题解链接https://blog.csdn.net/WJTF2/article/details/136239183?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522423e6dee0d2c53e9645ecba193312fb3%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257......
  • vscode软件基础使用教程
    visualstudiocode使用教程目录安装和配置编辑器配置推荐的插件常用的快捷键调试功能配置工作区设置比较细节的配置问题安装和配置安装地址visualstudiocode官方安装地址安装的过程中可以将添加到path中,之后就是可以在终端中通过输入code来打开visualstudio......
  • 贪吃蛇小游戏(c++)
    随手写的,一个十分有趣的贪吃蛇小游戏。用了随机数与二维数组实现。欢迎各位大佬提出修改意见#include<iostream>#include<cstdlib>usingnamespacestd;intx=0,y=0,bx,by,f=0;charmove;charmap[20][20];intbody[20][2];intifb=0;intmain(intargc,constcha......
  • 【新教程】Ubuntu 24.04 单节点安装slurm
    背景网上教程老旧,不适用。详细步骤1、安装slurmsudoaptinstallslurm-wlmslurm-wlm-doc-y检查是否安装成功:slurmd--version如果得到slurm-wlm23.11.4,表明安装成功。2、配置slurm。使用命令:sudovi/etc/slurm/slurm.conf在其中输入以下内容:ClusterName=cool......
  • TEMPLATE METHOD(模板方法)—类行为型模式
    1.意图定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。2.动机考虑一个提供Application和Document类的应用框架。Application类负责打开一......
  • C18.【C++ Cont】OJ测试用例的各种输入情况汇总
    目录1.思维导图2.单组测试用例3.多组测试用例1.测试数据组已知(输入)类模版例题2.测试数据组未知模版3.特殊值结束测试数据模版1.逐个字符处理2.一次读一行例题4.应对空格的处理方法1.一次读一行模板2.一次读一个单词5.应对数字的处理方法两个认知1.呈现在......
  • 数学建模学习-整数规划(Integer Programming)教程(3)
    数学建模学习-整数规划(IntegerProgramming)教程(3)写在最前注意本文的相关代码及例子为同学们提供参考,借鉴相关结构,在这里举一些通俗易懂的例子,方便同学们根据实际情况修改代码,很多同学私信反映能否添加一些可视化,这里每篇教程都尽可能增加一些可视化方便同学理解,但具体......
  • 【c++】函数调用机制
    【c++】函数调用机制1.建立栈帧空间2.传递数据,为局部变量分配空间3.保护现场,主调函数运行状态和返回值地址入栈4.执行被调函数体5.释放局部变量的栈空间6.恢复现场,取主调函数运行状态和返回值地址7.继续执行主调函数后续语句详细介绍:ebpespeax等均是寄存器1.......
  • 【c++】【Linux】堆和栈的区别
    【c++】【Linux】堆和栈的区别区别堆栈管理方式由程序员手动分配手动释放由系统自动管理生长方式从低地址向高地址增长从高地址向低地址增长空间大小32位linux下可占2.9G左右32位Linux下占10M左右windows下占1M左右存储内容动态分配的内存,常用于存储链表、对象等动态数据......
  • STM32F1基于HAL库的学习记录实用使用教程分享(四、OLED IIC)
    往期内容STM32F1基于HAL库的学习记录实用使用教程分享(一、GPIO_Output)STM32F1基于HAL库的学习记录实用使用教程分享(二、GPIO_Input按键)STM32F1基于HAL库的学习记录实用使用教程分享(三、外部中断按键)文章目录往期内容前言一、IIC1.概念2.IIC作用3.IIC的特点II......