C++ 泛型和模板
C++ 中的泛型编程技术包括模板和 STL(标准模板库),它们可以实现代码的通用性和重用性。本文将主要介绍 C++ 中的模板,以及如何使用它们来实现泛型编程。
模板基础
模板是一种将类型作为参数的编程技术。通过使用模板,可以编写通用的函数和类,从而使得编写的代码可以适用于多种数据类型。
函数模板
函数模板是一种通用的函数定义,其中一个或多个参数使用泛型类型。在调用函数模板时,需要指定实际类型。
示例代码:
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
int main() {
int result1 = max(3, 5); // result1 = 5
double result2 = max(3.14, 2.71); // result2 = 3.14
}
上述代码定义了一个函数模板 max
,它接受两个类型相同的参数,并返回它们中的最大值。使用关键字 template
和 typename
声明模板类型参数 T
,在函数体中使用模板类型参数 T
代表任意类型。
类模板
类模板是定义通用类的一种方式,其中一个或多个成员使用泛型类型。与函数模板类似,在使用类模板时需要指定实际类型。
示例代码:
template <typename T>
class Stack {
public:
void push(T val);
T pop();
private:
T data[100];
int top;
};
template <typename T>
void Stack<T>::push(T val) {
if (top < 100) {
data[top++] = val;
}
}
template <typename T>
T Stack<T>::pop() {
if (top > 0) {
return data[--top];
} else {
return T();
}
}
int main() {
Stack<int> int_stack;
int_stack.push(1);
int_stack.push(2);
int result1 = int_stack.pop(); // result1 = 2
int result2 = int_stack.pop(); // result2 = 1
Stack<std::string> str_stack;
str_stack.push("Hello");
str_stack.push("World");
std::string result3 = str_stack.pop(); // result3 = "World"
std::string result4 = str_stack.pop(); // result4 = "Hello"
}
上述代码定义了一个类模板 Stack
,它是一个栈数据结构,可以存储任意类型的数据。在类模板中声明成员函数时,需要在函数名前加上 template <typename T>
和类名 Stack<T>
表示成员函数属于类模板。
模板参数
模板参数可以是类型参数和非类型参数。类型参数包括类、结构体、指针、引用、枚举和模板类型参数;非类型参数包括整型常量、指向对象的指针和成员指针。
在使用模板时,可以将任何类型作为模板参数。如果模板参数是类或结构体,则需要提供相应的定义,以便编译器可以实例化该模板。
示例代码:
template <typename T, int size>
class Array {
public:
void set(int index, T val) {
if (index >= 0 && index < size) {
data[index] = val;
}
}
T get(int index) {
if (index >= 0 && index < size) {
return data[index];
} else {
return T();
}
}
private:
T data[size];
};
struct Point3D {
float x, y, z;
};
int main() {
Array<int, 10> int_arr;
int_arr.set(0, 1);
int_arr.set(1, 2);
int result1 = int_arr.get(0); // result1 = 1
Array<Point3D, 5> point_arr;
point_arr.set(0, {1.0f, 2.0f, 3.0f});
Point3D result2 = point_arr.get(0); // result2.x = 1.0f, result2.y = 2.0f, result2.z = 3.0f
}
上述代码定义了一个模板类 Array
,它包含一个大小为 size
的数据数组。在使用模板时,需要指定数据类型 T
和数组大小 size
。在这里,Array<int, 10>
表示一个包含 10 个整数的数组,而 Array<Point3D, 5>
表示一个包含 5 个 Point3D
结构体的数组。
模板实现
在使用模板时,需要将模板的定义和实现分别放在 .h
和 .cpp
文件中。由于编译器在编译时需要实例化模板,因此模板的定义和实现都需要在一个文件中。
例如,对于上述定义的类模板 Stack
,可以将其定义放在 stack.h
文件中:
template <typename T>
class Stack {
public:
void push(T val);
T pop();
private:
T data[100];
int top;
};
将其实现放在 stack.cpp
文件中:
#include "stack.h"
template <typename T>
void Stack<T>::push(T val) {
if (top < 100) {
data[top++] = val;
}
}
template <typename T>
T Stack<T>::pop() {
if (top > 0) {
return data[--top];
} else {
return T();
}
}
在使用 Stack
类时,需要在文件头部包含 stack.h
文件,并在程序中实例化模板:
#include "stack.h"
int main() {
Stack<int> int_stack;
int_stack.push(1);
int_stack.push(2);
int result1 = int_stack.pop(); // result1 = 2
int result2 = int_stack.pop(); // result2 = 1
Stack<std::string> str_stack;
str_stack.push("Hello");
str_stack.push("World");
std::string result3 = str_stack.pop(); // result3 = "World"
std::string result4 = str_stack.pop(); // result4 = "Hello"
}
需要注意的是,模板的所有代码都需要放在头文件中,以便编译器在使用模板时可以实例化该模板。因此,通常情况下,模板不会将定义和实现分开放在不同的文件中。