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

C++泛型编程2 - 类模板

C++ 类模板全面教程

类模板是C++中强大且灵活的特性,它允许我们创建可适用于多种数据类型的类。下面我将从基础到高级,系统性地介绍类模板。

一、类模板基本概念

1.1 什么是类模板

类模板是一种允许我们使用不同类型创建类的蓝图。就像函数模板可以生成针对不同类型参数的函数一样,类模板可以生成针对不同类型参数的类。

1.2 为什么需要类模板

假设你需要一个可以存储任何类型数据的栈,不使用模板就需要为每种类型分别创建类:

class IntStack { /* 实现int栈 */ };
class DoubleStack { /* 实现double栈 */ };
class StringStack { /* 实现string栈 */ };

使用类模板则可以定义一次,适应多种类型:

template <typename T>
class Stack { /* 通用栈实现 */ };

二、类模板基本语法

2.1 基本定义

template <typename T>    // 或者 template <class T>
class MyClass {// 类成员定义
};

2.2 示例:一个简单的Array类模板

template <typename T>
class Array {
private:T* ptr;int size;public:Array(int s) : size(s), ptr(new T[s]) {}~Array() { delete[] ptr; }T& operator[](int index) {if (index >= size || index < 0) {throw std::out_of_range("Index out of range");}return ptr[index];}int getSize() const { return size; }
};

2.3 使用类模板

Array<int> intArr(10);      // 创建存储int的数组
Array<double> doubleArr(5); // 创建存储double的数组

三、类模板的定义与使用

3.1 多个类型参数

template <typename T, typename U>
class Pair {
public:T first;U second;Pair(const T& f, const U& s) : first(f), second(s) {}
};Pair<int, double> p1(1, 3.14);
Pair<std::string, bool> p2("success", true);

3.2 默认类型参数

template <typename T, typename U = bool>
class Pair {
public:T first;U second;Pair(const T& f, const U& s) : first(f), second(s) {}
};Pair<int, double> p1(1, 3.14);
Pair<std::string> p2("success", true);	// 使用默认类型

3.3 默认非类型参数

template <typename T = int, int size = 10>
class Container {T items[size];// ...
};Container<> defaultContainer;          // 使用默认参数 T=int, size=10
Container<double, 20> customContainer; // 指定参数

3.4 成员函数模板

类内部可以定义成员函数模板:

template <typename T>
class Box {T content;public:template <typename U>void assign(const U& newContent) {content = newContent; // 需要U能隐式转换为T}
};

3.5 成员函数的类外实现

成员函数类外实现,模板声明必须和类模板一致,但无需给出默认参数。以为类名作用域需要指定类型,即必须用模板类型实例化,才能使用。

template <class T1, class T2>
class 类名{T1 函数名(T2 a, T2 b);
}// 注意类名作用域使用模板参数进行实例化。
template <class T1, class T2>
T1 类名<T1, T2>::函数名(T2 a, T2 b){// 函数实现
}

3.6 类模板实例化

与函数模板不同,类模板只能显式实例化,不能隐式实例化。因为函数模板调时可以根据参数自动判断类型,而类模板调用时除了显式指定,没有任何参考依据。

template <class T>
class Complex{T real;T img;
}// 使用类模板创建对象
Complex<int> c;

3.7 类模板作为函数参数

1. 普通函数

类模板在普通函数中使用必须实例化为一个具体的类:

template <class T>
class Complex{T real;T img;
}Complex<double> add(Complex<double> a, Complex<double> b);
2. 函数模板

方法一:常用,用泛型类型实例化类模板作为函数参数类型。

template <class T>
class Complex{T real;T img;
}template <class T>
Complex<T> add(Complex<T> a, Complex<T> b);

方法二:通用所有类,在使用时再指定类。

template <class T>
class Complex{T real;T img;
}template <class T>
T add(T a, T b);

四、类模板特化

4.1 全特化

所有类型参数都必须具体化。

template <>
class Array<bool> {
private:unsigned char* ptr;int size;public:// 特化实现,按位存储bool以节省空间Array(int s) : size(s), ptr(new unsigned char[(s+7)/8]) {}~Array() { delete[] ptr; }// 其他特化实现...
};

4.2 偏特化

只具体化部分类型参数。没有具体化的泛型类型在仍然在<>中。而类定义的实例化中可以使用泛型类型。

template <class T>
class 类名<T, string>{}

五、类模板中的静态成员

类模板中的static成员对于每个模板实例化都是独立的:

template <typename T>
class Counter {
public:static int count;Counter() { ++count; }~Counter() { --count; }
};template <typename T>
int Counter<T>::count = 0;Counter<int> c1, c2;    // Counter<int>::count == 2
Counter<double> c3;     // Counter<double>::count == 1

六、类模板继承

6.1 从类模板继承

template <typename T>
class Base {// ...
};template <typename T>
class Derived : public Base<T> {// ...
};

6.2 继承关系中的名称查找

在模板继承中需要使用this->Base<T>::来访问基类成员:

template <typename T>
class Derived : public Base<T> {
public:void foo() {this->baseMember();  // 使用this->访问基类成员Base<T>::baseMember(); // 或使用完整限定名}
};

七、类模板与友元

7.1 友元函数

template <typename T>
class MyClass {T value;// 声明友元函数friend void printValue(const MyClass<T>& obj) {std::cout << obj.value;}
};MyClass<int> obj;
printValue(obj); // 正确调用

7.2 友元类

template <typename T>
class FriendClass; // 前向声明template <typename T>
class MyClass {
private:T secret;friend class FriendClass<T>; // 声明友元类
};

八、使用技巧与最佳实践

  1. 分离实现与声明:可以将类模板的声明放在头文件,定义放在另一个头文件(如.tpp.ipp),然后在声明文件末尾包含定义文件。

  2. 防止模板膨胀:避免生成过多不必要的模板实例,可通过特化等方式优化。

  3. 明确错误信息:模板错误通常在实例化时才被发现,使用static_assert提供更友好的错误信息:

template <typename T>
class NumericArray {static_assert(std::is_arithmetic<T>::value, "NumericArray requires an arithmetic type");// ...
};

总结

类模板特点

  • 只能显式实例化,不能隐式实例化
  • 可以指定默认类型

类模板是C++泛型编程的核心技术之一,通过类模板我们可以:

  1. 编写与类型无关的通用代码
  2. 减少代码重复,提高复用性
  3. 在编译时完成类型检查,确保类型安全
  4. 通过特化提供特定类型的优化实现

掌握类模板能显著提升你的C++编程能力,特别是在开发和维护大型库时。建议从简单例子开始,逐步尝试更复杂的应用场景。

相关文章:

  • 基于LQR控制器的六自由度四旋翼无人机模型simulink建模与仿真
  • 「Java案例」古代重量单位转换
  • 力扣 刷题(第七十一天)
  • 湖北理元理律师事务所:债务优化中的法律理性与人文温度
  • MySQL 索引 -- 磁盘,主键索引,唯一索引,普通索引,全文索引
  • cocos creator 3.8 - 精品源码 - 挪车超人(挪车消消乐)
  • 【学习】《算法图解》第七章学习笔记:树
  • 设计模式(策略,工厂,单例,享元,门面)+模板方法
  • spring-ai-alibaba DashScopeCloudStore自动装配问题
  • Pytest自动化测试执行环境切换的2种解决方案
  • Word之空白页删除2
  • 访问不了/druid/index.html (sql.html 或 login.html)
  • 自学嵌入式 day26 - 系统编程 文件io 目录操作
  • Insar 相位展开真实的数据集的生成与下载(随机矩阵放大,zernike 仿真包裹相位)
  • 【编译原理】期末
  • 多表连接查询:语法、注意事项与最佳实践
  • Java+Python智能化网盘【Day9-1】
  • 趣味数据结构之——栈
  • 1 Studying《Computer Vision: Algorithms and Applications 2nd Edition》1-5
  • 基于springboot的海产品交易系统