【C++模板与泛型编程】模板定义

📅 2026/7/29 7:24:35 👁️ 阅读次数 📝 编程学习
【C++模板与泛型编程】模板定义

在 C++ 编程中,模板是实现泛型编程的核心机制。通过模板,我们可以创建通用的函数、类和算法,而不必预先指定具体的数据类型。通过模板,我们可以编写更加灵活、复用性更高的代码,极大地提升开发效率。

一、函数模板的定义与使用

1.1 基本概念

函数模板允许我们定义一个通用的函数,其参数类型和返回值类型可以是泛型的。语法格式如下:

代码语言:javascript

AI代码解释

template <typename T> T max(T a, T b) { return a > b ? a : b; }

其中,typename关键字也可以用class替代,二者在模板定义中没有区别。template <typename T>声明了一个模板参数T,它可以是任何类型。函数max可以接受两个类型为T的参数,并返回类型为T的结果。

1.2 模板参数推导

当调用函数模板时,编译器会根据传入的实参自动推导模板参数的类型:

代码语言:javascript

AI代码解释

int a = 5, b = 10; int result = max(a, b); // 自动推导 T 为 int double x = 3.14, y = 2.71; double d_result = max(x, y); // 自动推导 T 为 double
1.3 显式指定模板参数

在某些情况下,编译器无法自动推导模板参数,此时需要显式指定:

代码语言:javascript

AI代码解释

// 显式指定 T 为 double auto mixed_result = max<double>(a, x); // int 和 double 的混合比较
1.4 函数模板示例:通用交换函数

下面是一个通用的交换函数模板,它可以交换任意类型的两个变量:

代码语言:javascript

AI代码解释

template <typename T> void swap(T& a, T& b) { T temp = a; a = b; b = temp; } // 使用示例 int main() { int x = 5, y = 10; swap(x, y); // 交换两个 int double a = 3.14, b = 2.71; swap(a, b); // 交换两个 double std::string s1 = "hello", s2 = "world"; swap(s1, s2); // 交换两个 string return 0; }

二、类模板的定义与使用

2.1 基本概念

类模板允许我们定义一个通用的类,其中的成员变量和成员函数可以使用泛型类型。语法格式如下:

代码语言:javascript

AI代码解释

template <typename T> class Container { private: T value; public: Container(T val) : value(val) {} T getValue() const { return value; } void setValue(T val) { value = val; } };
2.2 实例化类模板

使用类模板时,必须显式指定模板参数的类型:

代码语言:javascript

AI代码解释

Container<int> intContainer(42); // T 被实例化为 int Container<double> doubleContainer(3.14); // T 被实例化为 double
2.3 类模板的成员函数

类模板的成员函数可以在类内部定义,也可以在类外部定义。在类外部定义时,需要使用完整的模板声明:

代码语言:javascript

AI代码解释

template <typename T> class Container { private: T value; public: Container(T val); T getValue() const; void setValue(T val); }; // 类外定义构造函数 template <typename T> Container<T>::Container(T val) : value(val) {} // 类外定义成员函数 template <typename T> T Container<T>::getValue() const { return value; } template <typename T> void Container<T>::setValue(T val) { value = val; }
2.4 类模板示例:动态数组

下面是一个简单的动态数组类模板实现:

代码语言:javascript

AI代码解释

template <typename T> class DynamicArray { private: T* data; size_t size; size_t capacity; public: DynamicArray() : data(nullptr), size(0), capacity(0) {} ~DynamicArray() { delete[] data; } void push_back(const T& value) { if (size >= capacity) { resize(capacity == 0 ? 1 : capacity * 2); } data[size++] = value; } T& operator[](size_t index) { return data[index]; } const T& operator[](size_t index) const { return data[index]; } size_t getSize() const { return size; } private: void resize(size_t newCapacity) { T* newData = new T[newCapacity]; for (size_t i = 0; i < size; ++i) { newData[i] = data[i]; } delete[] data; data = newData; capacity = newCapacity; } }; // 使用示例 int main() { DynamicArray<int> intArray; intArray.push_back(10); intArray.push_back(20); std::cout << "Array size: " << intArray.getSize() << std::endl; std::cout << "Element at index 0: " << intArray[0] << std::endl; DynamicArray<std::string> stringArray; stringArray.push_back("Hello"); stringArray.push_back("World"); std::cout << "Element at index 1: " << stringArray[1] << std::endl; return 0; }

三、模板形参详解

3.1 模板类型形参

模板类型形参是最常见的模板参数,使用typenameclass关键字声明:

代码语言:javascript

AI代码解释

template <typename T> // typename 和 class 在这里等价 class MyClass {};
3.2 非类型模板形参

非类型模板形参允许我们传递一个常量值作为模板参数。常见的非类型参数包括整数、指针和引用:

代码语言:javascript

AI代码解释

template <int N> class FixedArray { private: int data[N]; public: int& operator[](int index) { return data[index]; } const int& operator[](int index) const { return data[index]; } int size() const { return N; } }; // 使用示例 FixedArray<10> arr; // 创建一个大小为 10 的数组
3.3 模板模板形参

模板模板形参允许将一个模板作为另一个模板的参数:

代码语言:javascript

AI代码解释

template <template <typename> class Container, typename T> class Wrapper { private: Container<T> container; public: void add(const T& value) { container.push_back(value); } }; // 使用示例 #include <vector> #include <list> Wrapper<std::vector, int> vecWrapper; // 使用 vector 作为容器 Wrapper<std::list, double> listWrapper; // 使用 list 作为容器
3.4 模板参数默认值

模板参数可以有默认值,类似于函数参数的默认值:

代码语言:javascript

AI代码解释

template <typename T = int, int N = 10> class DefaultArray { private: T data[N]; public: // ... }; // 使用示例 DefaultArray<> defaultArray; // 使用默认参数:int, 10 DefaultArray<double, 20> customArray; // 自定义参数

四、重载操作符与模板结合

4.1 操作符重载基础

操作符重载允许我们为自定义类型重新定义操作符的行为。例如,为自定义类重载+操作符:

代码语言:javascript

AI代码解释

class Complex { private: double real, imag; public: Complex(double r = 0, double i = 0) : real(r), imag(i) {} // 重载 + 操作符 Complex operator+(const Complex& other) const { return Complex(real + other.real, imag + other.imag); } };
4.2 模板与操作符重载结合

当操作符重载与模板结合时,可以为泛型类提供通用的操作符行为:

代码语言:javascript

AI代码解释

template <typename T> class Pair { private: T first, second; public: Pair(const T& a, const T& b) : first(a), second(b) {} // 重载 + 操作符 Pair operator+(const Pair& other) const { return Pair(first + other.first, second + other.second); } };
4.3 友元函数与模板

友元函数可以与模板结合,实现更灵活的操作符重载:

代码语言:javascript

AI代码解释

template <typename T> class Point { private: T x, y; public: Point(T x = 0, T y = 0) : x(x), y(y) {} // 声明友元函数 friend Point operator+(const Point& a, const Point& b) { return Point(a.x + b.x, a.y + b.y); } };

五、类型转换与模板

5.1 隐式类型转换

模板类可以定义转换构造函数,实现从其他类型到模板类型的隐式转换:

代码语言:javascript

AI代码解释

template <typename T> class SmartPtr { private: T* ptr; public: // 转换构造函数 template <typename U> SmartPtr(U* p) : ptr(p) {} // ... }; // 使用示例 class Base {}; class Derived : public Base {}; SmartPtr<Base> ptr(new Derived); // 隐式转换
5.2 类型转换操作符

模板类也可以定义类型转换操作符,实现从模板类型到其他类型的转换:

代码语言:javascript

AI代码解释

template <typename T> class Number { private: T value; public: Number(T val) : value(val) {} // 类型转换操作符 template <typename U> operator U() const { return static_cast<U>(value); } }; // 使用示例 Number<int> num(42); double d = num; // 隐式转换为 double

六、编写泛型程序的最佳实践

6.1 遵循最小特权原则

模板应该只依赖于完成任务所需的最小接口,提高代码的通用性:

代码语言:javascript

AI代码解释

// 不好的设计:依赖具体类型的接口 template <typename T> void printAll(const std::vector<T>& vec) { for (const auto& elem : vec) { elem.print(); // 依赖 T 有 print() 方法 } } // 好的设计:只依赖必要的操作符 template <typename T> void printAll(const std::vector<T>& vec) { for (const auto& elem : vec) { std::cout << elem << std::endl; // 只依赖 << 操作符 } }
6.2 使用概念(Concepts)约束模板

C++20 引入的概念(Concepts)可以约束模板参数必须满足的条件:

代码语言:javascript

AI代码解释

// C++20 概念示例 template <typename T> concept Addable = requires(T a, T b) { { a + b } -> std::same_as<T>; }; template <Addable T> T sum(T a, T b) { return a + b; }
6.3 避免模板代码膨胀

过度使用模板可能导致代码膨胀,可以通过显式实例化和模板复用减少代码体积:

代码语言:javascript

AI代码解释

// 显式实例化 template class std::vector<int>; // 只在一处实例化 vector<int>

七、实战案例:实现一个通用矩阵类

下面是一个使用模板实现的通用矩阵类,展示了模板、操作符重载和类型转换的综合应用:

代码语言:javascript

AI代码解释

#include <iostream> template <typename T, size_t Rows, size_t Cols> class Matrix { private: T data[Rows][Cols]; public: // 默认构造函数 Matrix() { for (size_t i = 0; i < Rows; ++i) { for (size_t j = 0; j < Cols; ++j) { data[i][j] = T(); } } } // 元素访问 T& operator()(size_t i, size_t j) { return data[i][j]; } const T& operator()(size_t i, size_t j) const { return data[i][j]; } // 矩阵加法 template <typename U> Matrix<T, Rows, Cols> operator+(const Matrix<U, Rows, Cols>& other) const { Matrix<T, Rows, Cols> result; for (size_t i = 0; i < Rows; ++i) { for (size_t j = 0; j < Cols; ++j) { result(i, j) = data[i][j] + other(i, j); } } return result; } // 矩阵乘法(需要满足列数等于另一个矩阵的行数) template <size_t OtherCols> Matrix<T, Rows, OtherCols> operator*(const Matrix<T, Cols, OtherCols>& other) const { Matrix<T, Rows, OtherCols> result; for (size_t i = 0; i < Rows; ++i) { for (size_t j = 0; j < OtherCols; ++j) { for (size_t k = 0; k < Cols; ++k) { result(i, j) += data[i][k] * other(k, j); } } } return result; } // 添加打印函数 void print() const { for (size_t i = 0; i < Rows; ++i) { for (size_t j = 0; j < Cols; ++j) { std::cout << data[i][j] << "\t"; } std::cout << std::endl; } } };

使用示例:

代码语言:javascript

AI代码解释

int main() { Matrix<int, 2, 2> m1; m1(0, 0) = 1; m1(0, 1) = 2; m1(1, 0) = 3; m1(1, 1) = 4; Matrix<int, 2, 2> m2; m2(0, 0) = 5; m2(0, 1) = 6; m2(1, 0) = 7; m2(1, 1) = 8; std::cout << "Matrix m1:" << std::endl; m1.print(); std::cout << "\nMatrix m2:" << std::endl; m2.print(); Matrix<int, 2, 2> sum = m1 + m2; std::cout << "\nm1 + m2:" << std::endl; sum.print(); Matrix<int, 2, 2> product = m1 * m2; std::cout << "\nm1 * m2:" << std::endl; product.print(); return 0; }

八、总结

模板是 C++ 中最强大的特性之一,它使得我们能够编写高度通用、灵活的代码。通过函数模板和类模板