C++ 类模板
什么是类模板?
类模板是C++泛型编程的基础,它允许我们定义一个可以适用于多种数据类型的通用类。通过类模板,我们可以创建一个类的蓝图,而不必为每种数据类型都编写一个新的类定义。类模板与函数模板的概念类似,不同之处在于类模板用于创建通用类,而函数模板用于创建通用函数。
类模板是C++中实现代码复用和抽象的重要机制,它为STL(标准模板库)提供了基础。
类模板的基本语法
类模板的定义使用关键字template
,后跟尖括号<>
中的一个或多个类型参数。基本语法如下:
template <typename T> // 或者使用 template <class T>
class ClassName {
// 类的成员和方法定义
// 可以使用类型参数T
};
其中:
template
是声明模板的关键字typename T
或class T
表示引入一个类型参数TClassName
是类的名称- 类的定义中可以使用类型参数T
简单的类模板示例
让我们通过一个简单的例子来理解类模板的概念 - 创建一个通用的盒子类,可以存储任何类型的单个值:
#include <iostream>
using namespace std;
template <typename T>
class Box {
private:
T item;
public:
// 构造函数
Box() : item(T()) {}
Box(T value) : item(value) {}
// 设置和获取值的方法
void setItem(T value) { item = value; }
T getItem() const { return item; }
// 显示盒子内容
void displayItem() {
cout << "Box contains: " << item << endl;
}
};
int main() {
// 创建一个存储整数的盒子
Box<int> intBox(123);
intBox.displayItem(); // 输出: Box contains: 123
// 创建一个存储字符串的盒子
Box<string> stringBox("Hello, world!");
stringBox.displayItem(); // 输出: Box contains: Hello, world!
// 创建一个存储浮点数的盒子
Box<double> doubleBox;
doubleBox.setItem(3.14159);
doubleBox.displayItem(); // 输出: Box contains: 3.14159
return 0;
}
输出:
Box contains: 123
Box contains: Hello, world!
Box contains: 3.14159
在上面的例子中,我们定义了一个Box
类模板,它可以存储任何类型的值。我们分别创建了存储整数、字符串和浮点数的盒子对象。
类模板的使用详解
1. 类模板的实例化
当我们使用类模板时,需要通过提供具体的类型来实例化类模板,例如:
Box<int> intBox; // 创建一个存储int类型的Box
Box<string> strBox; // 创建一个存储string类型的Box
Box<MyClass> myBox; // 创建一个存储自定义类型MyClass的Box
编译器会根据提供的类型,生成相应的类定义。
2. 类模板中的成员函数
类模板中的成员函数可以在类内部定义,也可以在类外部定义。如果在类外部定义,需要使用完整的模板声明:
// 类内定义
template <typename T>
class Box {
public:
void setItem(T value) { item = value; } // 在类内部定义
T getItem() const; // 声明函数
private:
T item;
};
// 类外定义
template <typename T>
T Box<T>::getItem() const {
return item;
}
3. 多个类型参数
类模板可以有多个类型参数:
template <typename T, typename U>
class Pair {
private:
T first;
U second;
public:
Pair(T a, U b) : first(a), second(b) {}
T getFirst() const { return first; }
U getSecond() const { return second; }
void display() {
cout << "First: " << first << ", Second: " << second << endl;
}
};
int main() {
Pair<int, string> pair1(1, "One");
pair1.display(); // 输出: First: 1, Second: One
return 0;
}
4. 默认模板参数
类模板可以有默认参数,这样在使用时如果不指定类型,会使用默认类型:
template <typename T = int>
class Number {
private:
T value;
public:
Number(T val = T()) : value(val) {}
T getValue() const { return value; }
};
int main() {
Number<> num1(42); // 使用默认类型int
Number<double> num2(3.14); // 指定类型为double
cout << num1.getValue() << endl; // 输出: 42
cout << num2.getValue() << endl; // 输出: 3.14
return 0;
}
类模板的高级特性
1. 模板特化
有时我们需要为特定类型提供不同的实现,这时可以使用模板特化:
// 通用模板
template <typename T>
class Storage {
public:
void store(T value) {
cout << "Storing generic value" << endl;
}
};
// 为bool类型特化的模板
template <>
class Storage<bool> {
public:
void store(bool value) {
cout << "Storing boolean value: " << (value ? "true" : "false") << endl;
}
};
int main() {
Storage<int> intStorage;
intStorage.store(42); // 输出: Storing generic value
Storage<bool> boolStorage;
boolStorage.store(true); // 输出: Storing boolean value: true
return 0;
}
2. 非类型模板参数
类模板也可以有非类型参数,如整数常量:
template <typename T, int Size>
class Array {
private:
T data[Size];
public:
Array() {
for(int i = 0; i < Size; ++i) {
data[i] = T();
}
}
T& operator[](int index) {
if(index < 0 || index >= Size) {
throw out_of_range("Index out of range");
}
return data[index];
}
int getSize() const { return Size; }
};
int main() {
Array<int, 5> intArray;
for(int i = 0; i < intArray.getSize(); ++i) {
intArray[i] = i * 10;
}
for(int i = 0; i < intArray.getSize(); ++i) {
cout << intArray[i] << " ";
}
// 输出: 0 10 20 30 40
return 0;
}
实际应用案例
案例1:自定义智能指针
以下是一个简化的智能指针类模板示例,展示了类模板在资源管理中的应用:
#include <iostream>
using namespace std;
template <typename T>
class SmartPointer {
private:
T* ptr;
public:
// 构造函数
SmartPointer(T* p = nullptr) : ptr(p) {
cout << "Resource acquired" << endl;
}
// 析构函数 - 自动释放资源
~SmartPointer() {
if(ptr != nullptr) {
delete ptr;
cout << "Resource released" << endl;
}
}
// 重载解引用运算符
T& operator*() const {
return *ptr;
}
// 重载箭头运算符
T* operator->() const {
return ptr;
}
};
class Resource {
public:
void doSomething() {
cout << "Resource is being used" << endl;
}
};
int main() {
{
SmartPointer<Resource> sp(new Resource());
sp->doSomething(); // 使用资源
// 当sp离开作用域时,资源会自动释放
}
cout << "End of scope" << endl;
return 0;
}
输出:
Resource acquired
Resource is being used
Resource released
End of scope
这个例子展示了类模板如何用于创建泛型的资源管理工具。
案例2:栈数据结构
下面是一个使用类模板实现的简单栈数据结构:
#include <iostream>
#include <stdexcept>
using namespace std;
template <typename T, int MaxSize = 100>
class Stack {
private:
T elements[MaxSize];
int top;
public:
// 构造函数
Stack() : top(-1) {}
// 入栈操作
void push(const T& value) {
if(top >= MaxSize - 1) {
throw overflow_error("Stack overflow");
}
elements[++top] = value;
}
// 出栈操作
T pop() {
if(top < 0) {
throw underflow_error("Stack underflow");
}
return elements[top--];
}
// 查看栈顶元素
T peek() const {
if(top < 0) {
throw underflow_error("Stack is empty");
}
return elements[top];
}
// 检查栈是否为空
bool isEmpty() const {
return top < 0;
}
// 获取栈中元素数量
int size() const {
return top + 1;
}
};
int main() {
// 创建一个存储整数的栈
Stack<int> intStack;
// 添加一些元素
intStack.push(10);
intStack.push(20);
intStack.push(30);
cout << "Stack size: " << intStack.size() << endl;
cout << "Top element: " << intStack.peek() << endl;
// 弹出并打印所有元素
while(!intStack.isEmpty()) {
cout << "Popped: " << intStack.pop() << endl;
}
// 创建一个字符串栈,最大容量为5
Stack<string, 5> stringStack;
stringStack.push("Hello");
stringStack.push("World");
cout << "String stack size: " << stringStack.size() << endl;
cout << "Popped from string stack: " << stringStack.pop() << endl;
return 0;
}
输出:
Stack size: 3
Top element: 30
Popped: 30
Popped: 20
Popped: 10
String stack size: 2
Popped from string stack: World
类模板的优缺点
优点
- 代码重用:可以用同一套代码处理不同的数据类型。
- 类型安全:在编译时检查类型兼容性,避免运行时错误。
- 性能:模板代码在编译时展开,没有运行时开销。
- 抽象和通用性:提高代码的抽象程度和通用性。
缺点
- 编译时间:模板可能导致编译时间增加。
- 代码膨胀:每种类型都会生成一个独立的代码实例。
- 错误信息复杂:模板相关的编译错误通常难以理解。
- 接口要求:使用模板的类型必须支持模板中使用的所有操作。
总结
类模板是C++中实现泛型编程的强大工具,它允许我们创建能够处理不同数据类型的通用类。通过类模板,我们可以:
- 定义一次代码,适用于多种数据类型
- 保持类型安全性
- 提高代码的重用性和维护性
- 实现诸如容器、智能指针等高级数据结构和工具
掌握类模板是成为高级C++程序员的必要技能,特别是在使用标准模板库(STL)时,理解类模板的工作原理将帮助你更好地使用这些工具。
练习
- 创建一个类模板
Pair
,存储两个相同类型的值,并提供获取最大值和最小值的方法。 - 实现一个通用的队列类模板
Queue
,支持基本的入队、出队和查看操作。 - 创建一个
Matrix
类模板,可以表示任何数值类型的矩阵,并实现基本的矩阵运算(如加法和乘法)。 - 为
string
类型特化上述Matrix
类模板,使其能处理字符串矩阵。 - 创建一个
Repository
类模板,能够存储和检索任意类型的对象,并且可以通过ID查找对象。
进一步阅读
- 《C++ Primer》- 深入了解C++模板编程
- 《Effective C++》- 了解模板使用中的最佳实践
- 《C++ Templates: The Complete Guide》- 全面详细的模板编程指南
通过这些练习和进一步的学习,你将能够更加熟练地使用C++类模板,编写出高效、可维护的代码。