C++ 模板基础
什么是模板?
模板是C++中一种强大的特性,它允许程序员编写通用的、与类型无关的代码。换句话说,你可以编写一次代码,然后对不同的数据类型重复使用,而不需要为每种类型都编写一个新函数或类。模板是C++泛型编程的基础。
模板本身不是函数或类,而是编译器用来生成函数或类的"配方"。
模板的类型
在C++中,主要有两种类型的模板:
- 函数模板
- 类模板
我们将详细介绍这两种类型的模板。
函数模板
函数模板允许你创建一个函数,该函数可以接受任何类型的参数,而不需要为每种类型都编写一个新函数。
函数模板的基本语法
template <typename T>
T functionName(T parameter) {
// 函数体
return parameter;
}
这里,typename
关键字指示 T
是一个类型参数。你也可以使用关键字 class
代替 typename
。
函数模板示例:求两个数的最大值
#include <iostream>
using namespace std;
// 函数模板定义
template <typename T>
T findMax(T a, T b) {
return (a > b) ? a : b;
}
int main() {
// 使用整数类型
cout << "最大整数: " << findMax(10, 20) << endl;
// 使用浮点类型
cout << "最大浮点数: " << findMax(10.5, 20.7) << endl;
// 使用字符类型
cout << "字典序较大的字符: " << findMax('a', 'b') << endl;
return 0;
}
输出:
最大整数: 20
最大浮点数: 20.7
字典序较大的字符: b
函数模板的实例化
当你使用函数模板时,编译器会根据你传递的参数类型生成对应的函数实例。这个过程称为模板实例化。
例如,当你调用 findMax(10, 20)
时,编译器会生成 int findMax(int a, int b)
的实例。
多个类型参数的函数模板
函数模板可以有多个类型参数:
template <typename T1, typename T2>
void printValues(T1 a, T2 b) {
cout << "a = " << a << ", b = " << b << endl;
}
你可以这样使用它:
printValues(10, "Hello"); // a = 10, b = Hello
printValues(3.14, 42); // a = 3.14, b = 42
类模板
类模板允许你定义一个通用类,该类可以适用于任何类型。
类模板的基本语法
template <typename T>
class ClassName {
private:
T member;
public:
void setMember(T value) {
member = value;
}
T getMember() {
return member;
}
};
类模板示例:通用数组类
#include <iostream>
using namespace std;
template <typename T>
class Array {
private:
T* elements;
int size;
public:
// 构造函数
Array(int s) {
size = s;
elements = new T[size];
}
// 析构函数
~Array() {
delete[] elements;
}
// 设置元素
void setElement(int index, T value) {
if (index >= 0 && index < size) {
elements[index] = value;
}
}
// 获取元素
T getElement(int index) {
if (index >= 0 && index < size) {
return elements[index];
}
// 这里简化处理,实际应该抛出异常
return T();
}
// 获取数组大小
int getSize() {
return size;
}
};
int main() {
// 创建一个整数数组
Array<int> intArray(5);
for (int i = 0; i < 5; i++) {
intArray.setElement(i, i * 10);
}
cout << "整数数组的元素:" << endl;
for (int i = 0; i < intArray.getSize(); i++) {
cout << intArray.getElement(i) << " ";
}
cout << endl;
// 创建一个字符数组
Array<char> charArray(5);
charArray.setElement(0, 'H');
charArray.setElement(1, 'e');
charArray.setElement(2, 'l');
charArray.setElement(3, 'l');
charArray.setElement(4, 'o');
cout << "字符数组的元素:" << endl;
for (int i = 0; i < charArray.getSize(); i++) {
cout << charArray.getElement(i);
}
cout << endl;
return 0;
}
输出:
整数数组的元素:
0 10 20 30 40
字符数组的元素:
Hello
类模板的实例化
类似于函数模板,类模板在使用时也需要进行实例化。不同的是,必须在创建对象时明确指定模板参数类型:
Array<int> intArray(5); // 创建整数数组
Array<string> strArray(3); // 创建字符串数组
类模板与继承
类模板可以作为基类被继承,也可以继承其他类:
template <typename T>
class Base {
// 基类的实现
};
template <typename T>
class Derived : public Base<T> {
// 派生类的实现
};
模板参数
默认模板参数
与函数参数一样,模板参数也可以有默认值:
template <typename T = int>
class Container {
private:
T element;
public:
void setElement(T val) {
element = val;
}
T getElement() {
return element;
}
};
int main() {
Container<> c; // 使用默认类型 int
c.setElement(42);
cout << c.getElement() << endl; // 输出: 42
Container<double> d; // 使用 double 类型
d.setElement(3.14);
cout << d.getElement() << endl; // 输出: 3.14
return 0;
}
非类型模板参数
模板参数不限于类型,也可以是常量表达式:
template <typename T, int Size>
class StaticArray {
private:
T elements[Size];
public:
void setElement(int index, T value) {
if (index >= 0 && index < Size) {
elements[index] = value;
}
}
T getElement(int index) {
if (index >= 0 && index < Size) {
return elements[index];
}
return T();
}
int getSize() {
return Size;
}
};
int main() {
StaticArray<int, 5> arr;
for (int i = 0; i < arr.getSize(); i++) {
arr.setElement(i, i * i);
}
for (int i = 0; i < arr.getSize(); i++) {
cout << arr.getElement(i) << " ";
}
cout << endl; // 输出: 0 1 4 9 16
return 0;
}
模板特化
模板特化允许你为特定的类型提供特定的实现。
函数模板特化
#include <iostream>
#include <string>
using namespace std;
// 通用模板
template <typename T>
void print(T value) {
cout << "通用版本: " << value << endl;
}
// 针对 char* 的特化
template <>
void print<char*>(char* value) {
cout << "字符串特化版本: " << value << endl;
}
int main() {
int i = 42;
double d = 3.14;
char str[] = "Hello";
print(i); // 调用通用版本
print(d); // 调用通用版本
print(str); // 调用特化版本
return 0;
}
输出:
通用版本: 42
通用版本: 3.14
字符串特化版本: Hello
类模板特化
#include <iostream>
#include <string>
using namespace std;
// 通用模板
template <typename T>
class Container {
public:
void display() {
cout << "通用容器" << endl;
}
};
// 针对 string 类型的特化
template <>
class Container<string> {
public:
void display() {
cout << "字符串容器" << endl;
}
};
int main() {
Container<int> intContainer;
Container<double> doubleContainer;
Container<string> stringContainer;
intContainer.display(); // 输出: 通用容器
doubleContainer.display(); // 输出: 通用容器
stringContainer.display(); // 输出: 字符串容器
return 0;
}
实际应用场景
1. 容器类
模板最常见的应用是在容器类中,如STL中的vector
、list
、map
等。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
// 整型向量
vector<int> intVector;
intVector.push_back(10);
intVector.push_back(20);
intVector.push_back(30);
cout << "整型向量中的元素:" << endl;
for (int i : intVector) {
cout << i << " ";
}
cout << endl;
// 字符串向量
vector<string> strVector;
strVector.push_back("Hello");
strVector.push_back("World");
cout << "字符串向量中的元素:" << endl;
for (const string& s : strVector) {
cout << s << " ";
}
cout << endl;
return 0;
}
2. 智能指针
模板在智能指针的实现中也起着重要作用,如std::shared_ptr
和std::unique_ptr
。
#include <iostream>
#include <memory>
using namespace std;
class Resource {
public:
Resource() { cout << "Resource 被创建" << endl; }
~Resource() { cout << "Resource 被销毁" << endl; }
void doSomething() { cout << "Resource 正在执行某项操作" << endl; }
};
int main() {
cout << "使用智能指针:" << endl;
{
shared_ptr<Resource> res1 = make_shared<Resource>();
res1->doSomething();
{
shared_ptr<Resource> res2 = res1; // 共享所有权
cout << "引用计数: " << res1.use_count() << endl; // 输出: 2
}
cout << "内部作用域结束后的引用计数: " << res1.use_count() << endl; // 输出: 1
}
cout << "外部作用域结束" << endl;
return 0;
}
3. 算法函数
模板在算法函数中的应用,例如排序算法:
#include <iostream>
using namespace std;
// 冒泡排序模板函数
template <typename T>
void bubbleSort(T arr[], int size) {
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
T temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 打印数组的模板函数
template <typename T>
void printArray(T arr[], int size) {
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
int main() {
// 整型数组排序
int intArr[] = {5, 2, 9, 1, 7, 6};
int intSize = sizeof(intArr) / sizeof(int);
cout << "排序前的整型数组: ";
printArray(intArr, intSize);
bubbleSort(intArr, intSize);
cout << "排序后的整型数组: ";
printArray(intArr, intSize);
// 双精度数组排序
double doubleArr[] = {3.14, 1.1, 5.5, 0.3, 2.7};
int doubleSize = sizeof(doubleArr) / sizeof(double);
cout << "排序前的浮点数组: ";
printArray(doubleArr, doubleSize);
bubbleSort(doubleArr, doubleSize);
cout << "排序后的浮点数组: ";
printArray(doubleArr, doubleSize);
return 0;
}
输出:
排序前的整型数组: 5 2 9 1 7 6
排序后的整型数组: 1 2 5 6 7 9
排序前的浮点数组: 3.14 1.1 5.5 0.3 2.7
排序后的浮点数组: 0.3 1.1 2.7 3.14 5.5
模板的注意事项
-
编译错误:模板错误通常在实例化时才会被检测到,这可能导致复杂难懂的错误消息。
-
代码膨胀:过度使用模板可能导致代码膨胀,因为每个不同类型的实例化都会生成新的代码。
-
调试困难:模板代码通常更难调试,因为需要追踪特定类型的实例化。
-
编译时间:大量使用模板可能会导致编译时间增加。
总结
C++模板是实现泛型编程的关键特性,它允许我们编写与类型无关的代码,提高代码的重用性和灵活性。主要包括:
- 函数模板:创建适用于不同类型的函数
- 类模板:创建适用于不同类型的类
- 模板参数:可以是类型参数,也可以是非类型参数
- 模板特化:为特定类型提供特定实现
模板在标准库、容器、算法、智能指针等多个领域有广泛应用。掌握模板的基础知识对深入学习C++至关重要。
练习
-
创建一个函数模板
swap
,用于交换两个相同类型的变量值。 -
编写一个通用的
min
函数模板,返回两个值中的较小值。 -
创建一个类模板
Pair
,它可以存储两个不同类型的值。 -
实现一个类模板
Stack
,包含push
、pop
和isEmpty
操作。 -
为上面的
Stack
模板,编写一个针对char*
类型的特化版本。
学习资源
- C++ 官方文档:https://en.cppreference.com/w/cpp/language/templates
- C++ Primer 或 Effective C++ 等书籍的模板章节
- 在线学习平台如Coursera、Udemy上的C++高级课程