前言:
上一篇介绍了c++模板的简单使用, 这一篇将介绍一下c++类模板
先看一段代码:
#include <iostream>
#include <vector>
template<typename T>
class Stack {
private:
std::vector<T> elements;
public:
void push(const T& element) {
elements.push_back(element);
}
void pop() {
if (!elements.empty()) {
elements.pop_back();
}
}
T& top() {
if (!elements.empty()) {
return elements.back();
}
throw std::out_of_range("Stack<>::top(): empty stack");
}
bool empty() const {
return elements.empty();
}
};
int main() {
// 使用Stack模板创建一个整数栈
Stack<int> intStack;
intStack.push(1);
intStack.push(2);
intStack.push(3);
while (!intStack.empty()) {
std::cout << intStack.top() << " ";
intStack.pop();
}
std::cout << std::endl;
// 使用Stack模板创建一个字符栈
Stack<char> charStack;
charStack.push('a');
charStack.push('b');
charStack.push('c');
while (!charStack.empty()) {
std::cout << charStack.top() << " ";
charStack.pop();
}
std::cout << std::endl;
return 0;
}
这是一个简单的C++类模板示例,实现了一个通用的栈数据结构,其中Stack
类模板接受一个模板参数 T
,它表示栈中存储的元素类型。可以通过 Stack<int>
和 Stack<char>
来实例化具体的栈类。这样就可以像使用普通的栈一样使用这些实例化的对象。
接下来看看非类型模板参数的模板类:
#include <iostream>
template<int Size>
class Array {
private:
int data[Size];
public:
void set(int index, int value) {
if (index >= 0 && index < Size) {
data[index] = value;
} else {
std::cerr << "Index out of range" << std::endl;
}
}
int get(int index) const {
if (index >= 0 && index < Size) {
return data[index];
} else {
std::cerr << "Index out of range" << std::endl;
return -1; // or throw an exception
}
}
};
int main() {
// 使用模板类Array创建一个包含5个整数的数组
Array<5> intArray;
// 设置和获取数组元素
for (int i = 0; i < 5; ++i) {
intArray.set(i, i * 10);
}
for (int i = 0; i < 5; ++i) {
std::cout << "Element at index " << i << ": " << intArray.get(i) << std::endl;
}
return 0;
}
非类型模板参数可以将常量表达式作为模板参数传递给模板类。这使得在编译时能确定一些值,而不是在运行时。非类型模板参数可以是整数、枚举、指针或引用。在这个示例中,Array
是一个模板类,它接受一个非类型模板参数 Size
,表示数组的大小。在 main()
函数中,我们使用 Array<5>
实例化了一个包含5个整数的数组。因此,Size
在这个实例中被设置为5。然后我们设置了数组的元素并进行了输出。
一个模板类能同时具有类型模板参数和非类型模板参数,看下面这个案例:
#include <iostream>
template<typename T, int Capacity>
class Queue {
private:
T data[Capacity];
int frontIndex;
int rearIndex;
public:
Queue() : frontIndex(0), rearIndex(0) {}
void enqueue(const T& item) {
if (rearIndex < Capacity) {
data[rearIndex++] = item;
} else {
std::cerr << "Queue is full. Unable to enqueue." << std::endl;
}
}
T dequeue() {
if (frontIndex < rearIndex) {
return data[frontIndex++];
} else {
std::cerr << "Queue is empty. Unable to dequeue." << std::endl;
return T(); // default value for T
}
}
bool isEmpty() const {
return frontIndex == rearIndex;
}
bool isFull() const {
return rearIndex == Capacity;
}
};
int main() {
// 创建一个容量为5的整数队列
Queue<int, 5> intQueue;
// 将一些整数入队
for (int i = 1; i <= 5; ++i) {
intQueue.enqueue(i * 10);
}
// 出队并输出元素
while (!intQueue.isEmpty()) {
std::cout << "Dequeued: " << intQueue.dequeue() << std::endl;
}
// 尝试向已满的队列添加元素
for (int i = 1; i <= 6; ++i) {
intQueue.enqueue(i * 10);
}
return 0;
}
它以类型和一个常量值作为参数,在这个示例中,Queue
是一个模板类,它有两个模板参数:T
表示存储在队列中的元素类型,Capacity
表示队列的容量。在 main()
函数中,我们使用 Queue<int, 5>
创建了一个容量为5的整数队列。然后我们将一些整数入队,并出队并输出它们。最后,我们尝试向已满的队列添加元素,观察错误消息。
不过使用非类型模板时需要注意参数类型,
非类型模板参数不能是以下类型:
-
浮点数类型:浮点数运算通常涉及到复杂的计算和比较操作,在编译时确定浮点数值可能会导致不确定性和不稳定性,因此C++标准规定不允许使用浮点数类型作为非类型模板参数。
-
非常量表达式类型:非类型模板参数必须在编译时被确定,而运行时的变量是无法满足这一要求的。如果允许运行时变量作为非类型模板参数,会导致模板实例化的不确定性和可移植性问题。
-
字符串类型:C++标准中没有直接支持字符串作为非类型模板参数的机制。字符串的长度和内容可能是变化的,在编译时确定字符串值的复杂性和不确定性会导致编译器难以处理。
-
类类型:模板的非类型参数必须是在编译时可确定的常量表达式,而对象是在运行时创建的。如果允许类类型作为非类型模板参数,就会涉及到对象的创建和析构等操作,这是在编译时无法完成的。
-
非静态成员指针类型:非静态成员函数指针涉及到类的实例,而模板实例化时并不会生成类的实例,因此无法使用非静态成员函数指针作为非类型模板参数。