马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
C++ 的类模板是泛型编程的焦点特性之一, 它让我们能够编写适用于多种范例的通用代码, 从而提高代码的复用性和扩展性.
本教程通过栈的实现为例, 深入探讨类模板的实现, 使用, 以及特化, 偏特化, 默认参数和范例别名等高级特性, 资助您更全面地掌握这一强盛工具.
1. 实现一个 Stack
笔者选择用一个栈的模板来做演示, 由于栈的接口和功能相对而言比较简朴. Stack模板支持如下操纵:
- Push: 入栈一个元素
- Pop: 弹出一个元素
- Top: 获取栈顶的元素
- Empty: 栈是否为空
- 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];
- }
- };
复制代码 从上面的代码可以看到
- 模板类相比平凡类多了一个泛型声明: template <typename T>
- 在模板类的内部, 数据成员大概函数成员都可用使用泛型T
- 类模板的实现通常是在一个头文件中, 不需要像平凡类举行头文件/源文件分离.
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++ 类模板的实现与特性. 盼望这些内容能资助您更好地明白并应用类模板.
源码链接
源码链接
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |