1. 泛型编程
泛型编程(Generic Programming)是一种编程范式,它允许程序员编写与数据类型无关的代码。通过使用泛型,程序员可以编写灵活、可重用的函数、类或数据结构,这些函数、类或数据结构可以操作多种类型的数据,而无需为每种数据类型编写单独的代码。泛型编程的核心思想是将算法与数据类型分离,使得算法可以独立于数据类型之外进行编写和测试。
【特点】
【示例】
下面是一个简单的泛型函数示例,该函数用于交换两个变量的值:
template<typename T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main() {
int x = 1, y = 2;
swap(x, y); // 调用模板函数,T被推导为int
double a = 3.14, b = 2.71;
swap(a, b); // 调用模板函数,T被推导为double
return 0;
}
在这个例子中,
swap
函数是一个模板函数,它接受两个类型为T
的参数(T
是一个占位符,代表任意类型)。在main
函数中,我们分别用int
和double
类型的变量调用了swap
函数,编译器会根据调用时的实际类型自动推导T
的类型,并生成相应的代码。
函数模板是C++中泛型编程的一种实现方式,它允许你定义一个与类型无关的函数。通过使用模板参数,你可以编写一个函数模板,该函数模板可以处理多种类型的数据,而无需为每种数据类型都编写一个单独的函数。
函数模板的定义使用template
关键字开始,后跟一个或多个模板参数(这些参数通常被放在尖括号<>
中),最后是函数返回类型、函数名和参数列表。模板参数可以是类型参数(如typename T
或class T
),也可以是非类型参数(如int N
),但在函数模板中,最常见的是类型参数。
template<typename T> // 或 template<class T>,两者在函数模板中几乎等价
返回类型 函数名(参数列表) {
// 函数体
}
【示例】
以下是一个简单的函数模板示例,用于计算两个值的最大值:
#include <iostream>
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << "The max of 5 and 3 is " << max(5, 3) << std::endl;
std::cout << "The max of 5.5 and 4.2 is " << max(5.5, 4.2) << std::endl;
// 注意:以下调用将引发编译错误,因为max(5, 3.14)的参数类型不同
// std::cout << "The max of 5 and 3.14 is " << max(5, 3.14) << std::endl;
return 0;
}
在这个例子中,max
函数模板接受两个类型为T
的参数,并返回这两个参数中较大的一个。在main
函数中,我们分别用int
和double
类型的参数调用了max
函数模板,编译器根据调用时的实际类型自动推导T
的类型,并生成相应的函数实例。
【 模板参数推导】
在调用函数模板时,编译器会自动推导模板参数的类型。这个过程通常基于函数实参的类型进行。如果编译器无法从函数实参中唯一地推导出模板参数的类型,或者推导出的类型不符合模板参数的要求(比如,模板参数有约束),则会导致编译错误。
通过结合使用函数模板、模板特化和模板重载,你可以创建出既灵活又高效的代码,以适应不同的数据类型和场景。
类模板是C++中泛型编程的另一种重要方式,它允许你定义与类型无关的类。与函数模板类似,类模板使用template
关键字后跟模板参数列表来定义。这些模板参数通常是类型参数,但也可以是非类型参数(如整数常量)。类模板可以在类定义中使用这些模板参数来指定成员变量的类型或成员函数的返回类型、参数类型等。
template<typename T, typename U> // 可以有多个模板参数
class ClassName {
public:
T memberVar; // 成员变量使用模板参数类型
U anotherMemberVar;
ClassName(T val1, U val2); // 构造函数
// 其他成员函数
void someFunction(T param);
};
// 构造函数的实现
template<typename T, typename U>
ClassName<T, U>::ClassName(T val1, U val2) : memberVar(val1), anotherMemberVar(val2) {
// 构造函数体
}
// 成员函数的实现
template<typename T, typename U>
void ClassName<T, U>::someFunction(T param) {
// 函数体
}
以下是一个简单的类模板示例,用于创建一个简单的栈容器:
#include <iostream>
template<typename T>
class Stack {
private:
T* elements;
size_t top;
size_t capacity;
public:
Stack(size_t capacity) : top(0), capacity(capacity) {
elements = new T[capacity];
}
~Stack() {
delete[] elements;
}
void push(const T& element) {
if (top < capacity) {
elements[top++] = element;
} else {
std::cerr << "Stack overflow!" << std::endl;
}
}
T pop() {
if (top > 0) {
return elements[--top];
} else {
std::cerr << "Stack underflow!" << std::endl;
// 注意:这里需要返回某种默认值或抛出异常,具体取决于你的需求
return T(); // 返回T类型的默认值
}
}
bool isEmpty() const {
return top == 0;
}
// 其他成员函数...
};
int main() {
Stack<int> intStack(10);
intStack.push(1);
intStack.push(2);
std::cout << "Popped: " << intStack.pop() << std::endl;
Stack<std::string> stringStack(5);
stringStack.push("Hello");
stringStack.push("World");
std::cout << "Popped: " << stringStack.pop() << std::endl;
return 0;
}
在这个例子中,我们定义了一个名为Stack
的类模板,它接受一个类型参数T
,用于指定栈中存储的元素的类型。然后,我们在main
函数中分别创建了一个int
类型的栈和一个std::string
类型的栈,并展示了如何使用它们。
【模板特化】
类模板特化允许你为特定的类型提供类的定制版本。这在你需要为某些类型提供特殊的实现或优化时非常有用。特化可以是完全特化(指定所有模板参数的类型)或偏特化(只指定部分模板参数的类型)。
【模板实例化】
当你使用类模板时,编译器会根据你提供的类型参数生成类的具体实例。这个过程称为模板实例化。在上面的例子中,Stack<int>
和Stack<std::string>
就是Stack
类模板的两个实例化。