- C++
C++ 泛型编程
- 2025-5-30 8:11:29 @
🧠 C++ 泛型编程
📚 一、什么是泛型编程?
泛型编程(Generic Programming) 是一种以类型无关的方式编写程序的技术,它允许我们写出适用于多种数据类型的通用代码。
在 C++ 中,模板(Template) 是实现泛型编程的核心机制。通过模板,我们可以为函数和类定义“蓝图”,编译器会根据实际使用的类型自动生成对应的代码。
🧩 二、为什么需要泛型编程?
✅ 优点:
- 代码复用:一个函数或类可以处理多个类型。
- 类型安全:编译时检查类型,避免运行时错误。
- 性能优化:模板生成的代码是静态绑定的,几乎无额外开销。
❌ 没有泛型的情况:
int maxInt(int a, int b) {
return (a > b) ? a : b;
}
double maxDouble(double a, double b) {
return (a > b) ? a : b;
}
如果支持 float
、long long
等,就需要写很多重复代码。这时候就要引入泛型!
🔁 三、函数模板 Function Template
✅ 基本语法:
template <typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
template <typename T>
:声明这是一个模板函数,T
是类型占位符。- 使用时无需指定类型,编译器自动推导。
💻 示例使用:
int main() {
std::cout << max(3, 5) << "\n"; // T -> int
std::cout << max(3.14, 2.718) << "\n";// T -> double
std::cout << max('A', 'B') << "\n"; // T -> char
return 0;
}
🧱 四、类模板 Class Template
当我们希望一个类能处理不同类型的数据时,就可以使用类模板。
✅ 示例:通用栈类
template <typename T>
class Stack {
private:
std::vector<T> elements;
public:
void push(const T& value) { elements.push_back(value); }
void pop() { elements.pop_back(); }
const T& top() const { return elements.back(); }
bool empty() const { return elements.empty(); }
};
💻 使用示例:
int main() {
Stack<int> intStack;
intStack.push(1);
intStack.push(2);
std::cout << "Top: " << intStack.top() << "\n";
Stack<std::string> stringStack;
stringStack.push("Hello");
stringStack.push("World");
std::cout << "Top: " << stringStack.top() << "\n";
}
🧬 五、多参数模板 Multiple Template Parameters
你可以为模板指定多个类型参数:
template <typename Key, typename Value>
class Pair {
private:
Key key;
Value value;
public:
Pair(Key k, Value v) : key(k), value(v) {}
void print() const {
std::cout << "[" << key << " : " << value << "]\n";
}
};
💻 使用:
Pair<std::string, int> p("Age", 25);
p.print(); // 输出 [Age : 25]
⚙️ 六、默认模板参数 Default Template Arguments
你可以在模板中为类型设置默认值:
template <typename T = int>
class Box {
private:
T data;
public:
Box(T d) : data(d) {}
void show() const { std::cout << data << "\n"; }
};
💻 使用:
Box<> box1(100); // 默认 int
Box<double> box2(3.14); // 显式 double
🧮 七、非类型模板参数 Non-type Template Parameters
除了类型,还可以传递常量值作为模板参数:
template <int N>
class Array {
private:
int data[N];
public:
void fill(int val) {
for (int i = 0; i < N; ++i)
data[i] = val;
}
void print() const {
for (int i = 0; i < N; ++i)
std::cout << data[i] << " ";
std::cout << "\n";
}
};
💻 使用:
Array<5> arr;
arr.fill(10);
arr.print(); // 输出 10 10 10 10 10
🔄 八、模板特化 Template Specialization
有时我们需要为特定类型提供不同的实现,这就是模板特化。
✅ 部分特化 Partial Specialization(仅限类模板)
template <typename T>
class Container {
public:
void info() { std::cout << "General container\n"; }
};
// 特化指针类型
template <typename T>
class Container<T*> {
public:
void info() { std::cout << "Pointer container\n"; }
};
💻 使用:
Container<int> c1;
c1.info(); // General container
Container<int*> c2;
c2.info(); // Pointer container
🎯 九、STL 中的泛型应用(标准库)
C++ 标准库大量使用了泛型编程,例如:
容器 | 描述 |
---|---|
std::vector<T> |
动态数组 |
std::map<K,V> |
键值对容器 |
std::list<T> |
双向链表 |
std::set<T> |
不可重复元素集合 |
这些容器都使用了模板技术,使得它们可以适应任意类型。
🧪 十、实战练习题
✅ 练习 1:编写一个通用交换函数
template <typename T>
void swapValues(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
✅ 练习 2:实现一个通用排序算法(冒泡排序)
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])
std::swap(arr[j], arr[j + 1]);
}
🧠 十一、常见误区与注意事项
问题 | 解决方法 |
---|---|
编译错误提示太长 | 多看模板实例化后的代码 |
类型不匹配 | 确保传入类型支持操作符 |
模板不能分离 .h 和 .cpp |
模板实现必须放在头文件中 |
函数模板无法重载 | 可以通过偏特化或 SFINAE 技术解决 |
📈 十二、高级主题(进阶)
- SFINAE(Substitution Failure Is Not An Error)
- Concepts(C++20)
- 模板元编程 TMP(Template Metaprogramming)
- Policy-based 设计模式
📘 总结
主题 | 内容 |
---|---|
泛型编程 | 用模板编写类型无关的代码 |
函数模板 | 支持通用函数 |
类模板 | 支持通用类 |
特化与偏特化 | 为特定类型定制行为 |
STL 应用 | vector、map、set 等 |
高级技巧 | Concepts、SFINAE、TMP |
📚 推荐学习资源
- 📖《C++ Primer》第五版
- 📖《Effective Modern C++》Scott Meyers
- 📘 cppreference.com(权威文档)
- 🧪 LeetCode / HackerRank 上练习模板题目
🎉 恭喜你完成了本教程!现在你可以编写自己的泛型库啦!
3 条评论
-
admin SU @ 2025-6-2 20:44:33
#include<iostream> using namespace std; template <int N> struct Array { private: int data[N]; public: void fill(int val) { for (int i = 0; i < N; ++i) data[i] = val; } void print() const { for (int i = 0; i < N; ++i) cout << data[i] << " "; cout << "\n"; } }; int main() { Array<5> arr; arr.fill(10); arr.print(); // 输出 10 10 10 10 10 return 0; }
-
2025-6-2 20:35:02@
#include<iostream> #include<vector> using namespace std; template <typename T> struct Stack { //public: void push(const T& value) { elements.push_back(value); } void pop() { elements.pop_back(); } const T& top() const { return elements.back(); } bool empty() const { return elements.empty(); } private: vector<T> elements; }; int main() { Stack<int> t; int n; cin >> n; while(n!=0){ t.push(n%8); n/=8; } while(!t.empty()){ cout<<t.top(); t.pop(); } return 0; }
在C++中,
const
是一个类型修饰符,用于表示“常量”或“不可修改”。在你提供的代码中,const
出现在几个不同的上下文中,每个都有特定的含义:1. 成员函数后的
const
const T& top() const; bool empty() const;
这里的
const
表示这些成员函数是“常量成员函数”,即它们承诺不会修改对象的状态。具体来说:const T& top() const
:返回栈顶元素的常量引用(防止修改栈顶元素),并且函数本身不会修改对象。bool empty() const
:检查栈是否为空,同样不会修改对象。
2. 参数列表中的
const
void push(const T& value);
这里的
const T&
表示参数value
是一个常量引用:- 函数内部不能修改
value
的值。 - 使用引用避免了对象的拷贝,提高效率。
3. 返回类型中的
const
const T& top() const;
返回值是一个
const T&
,即常量引用:- 调用者不能通过返回的引用修改栈顶元素。
- 例如:
t.top() = 10;
会导致编译错误。
为什么需要这些
const
?- 安全性:防止意外修改对象状态或传入的参数。
- 兼容性:常量对象(如
const Stack<int> s;
)只能调用const
成员函数。 - 良好的设计:明确函数的副作用,提高代码可读性。
示例说明
在你的代码中:
Stack<int> t; // ... const T& top() const { return elements.back(); }
top()
返回的是const int&
,防止你直接修改栈顶元素。- 如果没有这个
const
,你可以写t.top() = 10;
,这会破坏栈的封装性(栈顶元素应该只能通过push
和pop
修改)。
总结:
const
在C++中用于确保数据的不可变性,是一种重要的类型安全机制。 -
2025-5-30 8:11:41@
C++ 泛型编程完整教程
一、泛型编程概述
1.1 什么是泛型编程?
泛型编程(Generic Programming)是一种编程范式,它允许编写不依赖于具体数据类型的代码。通过泛型编程,我们可以创建通用的算法和数据结构,这些算法和数据结构能够处理多种数据类型,而不需要为每种数据类型单独编写代码。
1.2 为什么需要泛型编程?
- 代码复用:避免为不同数据类型重复编写相似的代码
- 类型安全:在编译时进行类型检查,减少运行时错误
- 性能优化:通过模板实例化生成针对特定类型的高效代码
1.3 C++ 中的泛型编程实现方式
C++ 主要通过以下两种方式实现泛型编程:
- 模板(Templates):函数模板和类模板
- 标准模板库(STL):包含容器、算法、迭代器等泛型组件
二、函数模板
2.1 函数模板的定义与使用
函数模板允许创建一个通用的函数,能够处理多种数据类型。
#include <iostream> using namespace std; // 函数模板:交换两个变量的值 template <typename T> void swap(T& a, T& b) { T temp = a; a = b; b = temp; } int main() { // 交换整数 int x = 10, y = 20; cout << "Before swap: x = " << x << ", y = " << y << endl; swap(x, y); // 自动推导类型为int cout << "After swap: x = " << x << ", y = " << y << endl; // 交换浮点数 double a = 3.14, b = 2.71; cout << "\nBefore swap: a = " << a << ", b = " << b << endl; swap(a, b); // 自动推导类型为double cout << "After swap: a = " << a << ", b = " << b << endl; // 显式指定模板参数类型 string s1 = "Hello", s2 = "World"; cout << "\nBefore swap: s1 = " << s1 << ", s2 = " << s2 << endl; swap<string>(s1, s2); // 显式指定类型为string cout << "After swap: s1 = " << s1 << ", s2 = " << s2 << endl; return 0; }
2.2 函数模板的重载
函数模板可以像普通函数一样被重载。
#include <iostream> using namespace std; // 函数模板:返回两个值中的较大值 template <typename T> T max(T a, T b) { return (a > b) ? a : b; } // 重载函数模板:处理C风格字符串 const char* max(const char* a, const char* b) { return (strcmp(a, b) > 0) ? a : b; } int main() { int x = 10, y = 20; cout << "Max of " << x << " and " << y << " is " << max(x, y) << endl; double a = 3.14, b = 2.71; cout << "Max of " << a << " and " << b << " is " << max(a, b) << endl; const char* s1 = "apple"; const char* s2 = "banana"; cout << "Max of " << s1 << " and " << s2 << " is " << max(s1, s2) << endl; return 0; }
2.3 多模板参数的函数模板
函数模板可以有多个模板参数。
#include <iostream> using namespace std; // 多模板参数的函数模板 template <typename T, typename U> T convert(U value) { return static_cast<T>(value); } int main() { double d = 3.14; int i = convert<int>(d); // 显式指定目标类型 cout << "Converted " << d << " to " << i << endl; float f = 2.71f; long l = convert<long, float>(f); // 显式指定两个模板参数 cout << "Converted " << f << " to " << l << endl; return 0; }
三、类模板
3.1 类模板的定义与使用
类模板允许创建通用的类,能够处理多种数据类型。
#include <iostream> using namespace std; // 类模板:动态数组 template <typename T> class Array { private: T* data; // 指向数组的指针 int size; // 当前元素个数 int capacity; // 数组容量 // 扩容函数 void resize(int newCapacity) { T* newData = new T[newCapacity]; for (int i = 0; i < size; i++) { newData[i] = data[i]; } delete[] data; data = newData; capacity = newCapacity; } public: // 构造函数 Array(int initialCapacity = 10) : size(0), capacity(initialCapacity) { data = new T[capacity]; } // 析构函数 ~Array() { delete[] data; } // 获取元素个数 int getSize() const { return size; } // 获取容量 int getCapacity() const { return capacity; } // 判断数组是否为空 bool isEmpty() const { return size == 0; } // 在末尾添加元素 void add(T element) { if (size == capacity) { resize(2 * capacity); } data[size++] = element; } // 获取指定位置的元素 T get(int index) const { if (index < 0 || index >= size) { throw out_of_range("Index out of range"); } return data[index]; } // 设置指定位置的元素值 void set(int index, T value) { if (index < 0 || index >= size) { throw out_of_range("Index out of range"); } data[index] = value; } // 打印数组内容 void print() const { cout << "Array: size = " << size << ", capacity = " << capacity << endl; cout << "["; for (int i = 0; i < size; i++) { cout << data[i]; if (i != size - 1) { cout << ", "; } } cout << "]" << endl; } }; int main() { // 创建存储整数的数组 Array<int> intArray; for (int i = 0; i < 5; i++) { intArray.add(i); } cout << "Integer Array:" << endl; intArray.print(); // 创建存储字符串的数组 Array<string> stringArray; stringArray.add("Hello"); stringArray.add("World"); cout << "\nString Array:" << endl; stringArray.print(); return 0; }
3.2 类模板的特化
当模板处理特定类型时,可能需要特殊的实现。这时可以使用类模板特化。
#include <iostream> #include <cstring> using namespace std; // 通用类模板 template <typename T> class Storage { private: T data; public: Storage(const T& value) : data(value) { cout << "Generic Storage constructor" << endl; } void print() const { cout << "Stored value: " << data << endl; } }; // 类模板特化:针对char*类型 template <> class Storage<char*> { private: char* data; public: Storage(const char* value) { cout << "Specialized Storage constructor for char*" << endl; if (value == nullptr) { data = nullptr; } else { // 为字符串分配内存并复制内容 data = new char[strlen(value) + 1]; strcpy(data, value); } } ~Storage() { delete[] data; } void print() const { cout << "Stored string: " << (data ? data : "null") << endl; } }; int main() { // 使用通用模板 Storage<int> intStorage(42); intStorage.print(); // 使用特化模板 Storage<char*> strStorage("Hello, World!"); strStorage.print(); return 0; }
3.3 类模板的部分特化
C++ 允许对类模板进行部分特化,即只特化部分模板参数。
#include <iostream> using namespace std; // 通用类模板 template <typename T, typename U> class Pair { public: Pair(const T& first, const U& second) : first(first), second(second) { cout << "Generic Pair constructor" << endl; } void print() const { cout << "First: " << first << ", Second: " << second << endl; } private: T first; U second; }; // 部分特化:当T和U相同时 template <typename T> class Pair<T, T> { public: Pair(const T& first, const T& second) : first(first), second(second) { cout << "Partial specialized Pair constructor (same types)" << endl; } void print() const { cout << "First and Second (same type): " << first << ", " << second << endl; } private: T first; T second; }; // 部分特化:当U为int时 template <typename T> class Pair<T, int> { public: Pair(const T& first, const int& second) : first(first), second(second) { cout << "Partial specialized Pair constructor (U is int)" << endl; } void print() const { cout << "First: " << first << ", Second (int): " << second << endl; } private: T first; int second; }; int main() { // 使用通用模板 Pair<double, string> p1(3.14, "Hello"); p1.print(); // 使用相同类型的部分特化 Pair<int, int> p2(10, 20); p2.print(); // 使用U为int的部分特化 Pair<string, int> p3("World", 42); p3.print(); return 0; }
四、标准模板库(STL)
4.1 STL 概述
STL 是 C++ 标准库的重要组成部分,提供了一系列通用的模板类和函数,包括容器、算法、迭代器、函数对象等。
STL 的主要组件:
- 容器(Containers):用于存储数据的类模板
- 算法(Algorithms):操作容器中数据的函数模板
- 迭代器(Iterators):提供访问容器中元素的方法
- 函数对象(Function Objects):重载了函数调用运算符的类
- 适配器(Adapters):修改其他组件接口的组件
- 分配器(Allocators):负责内存分配和释放的组件
4.2 容器
STL 提供了多种容器,分为顺序容器、关联容器和容器适配器。
4.2.1 顺序容器
顺序容器按线性顺序存储元素。
#include <iostream> #include <vector> #include <list> #include <deque> using namespace std; int main() { // vector:动态数组 vector<int> vec; vec.push_back(10); vec.push_back(20); vec.push_back(30); cout << "Vector: "; for (int i : vec) { cout << i << " "; } cout << endl; // list:双向链表 list<string> lst; lst.push_back("apple"); lst.push_front("banana"); lst.push_back("cherry"); cout << "List: "; for (const string& s : lst) { cout << s << " "; } cout << endl; // deque:双端队列 deque<double> dq; dq.push_back(1.1); dq.push_front(2.2); dq.push_back(3.3); cout << "Deque: "; for (double d : dq) { cout << d << " "; } cout << endl; return 0; }
4.2.2 关联容器
关联容器按键存储元素,支持高效的查找操作。
#include <iostream> #include <map> #include <set> using namespace std; int main() { // map:键值对映射(有序) map<string, int> ages; ages["Alice"] = 25; ages["Bob"] = 30; ages["Charlie"] = 22; cout << "Map:" << endl; for (const auto& pair : ages) { cout << pair.first << ": " << pair.second << endl; } // set:唯一元素集合(有序) set<int> numbers; numbers.insert(5); numbers.insert(2); numbers.insert(8); numbers.insert(2); // 重复元素会被忽略 cout << "\nSet: "; for (int num : numbers) { cout << num << " "; } cout << endl; // unordered_map:键值对映射(无序,基于哈希表) unordered_map<string, double> prices; prices["apple"] = 1.5; prices["banana"] = 0.8; prices["cherry"] = 3.2; cout << "\nUnordered Map:" << endl; for (const auto& pair : prices) { cout << pair.first << ": " << pair.second << endl; } return 0; }
4.2.3 容器适配器
容器适配器提供了特定的接口来操作底层容器。
#include <iostream> #include <stack> #include <queue> using namespace std; int main() { // stack:后进先出(LIFO) stack<int> st; st.push(10); st.push(20); st.push(30); cout << "Stack: "; while (!st.empty()) { cout << st.top() << " "; st.pop(); } cout << endl; // queue:先进先出(FIFO) queue<string> q; q.push("apple"); q.push("banana"); q.push("cherry"); cout << "Queue: "; while (!q.empty()) { cout << q.front() << " "; q.pop(); } cout << endl; // priority_queue:优先队列(最大堆) priority_queue<int> pq; pq.push(5); pq.push(2); pq.push(8); cout << "Priority Queue: "; while (!pq.empty()) { cout << pq.top() << " "; pq.pop(); } cout << endl; return 0; }
4.3 迭代器
迭代器是一种对象,用于遍历容器中的元素。
#include <iostream> #include <vector> using namespace std; int main() { vector<int> vec = {10, 20, 30, 40, 50}; // 使用正向迭代器 cout << "Forward iteration: "; for (vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) { cout << *it << " "; } cout << endl; // 使用常量迭代器(只读) cout << "Constant iteration: "; for (vector<int>::const_iterator cit = vec.cbegin(); cit != vec.cend(); ++cit) { cout << *cit << " "; } cout << endl; // 使用反向迭代器 cout << "Reverse iteration: "; for (vector<int>::reverse_iterator rit = vec.rbegin(); rit != vec.rend(); ++rit) { cout << *rit << " "; } cout << endl; // 使用C++11的范围for循环(底层使用迭代器) cout << "Range-based for loop: "; for (int num : vec) { cout << num << " "; } cout << endl; return 0; }
4.4 算法
STL 提供了大量的通用算法,可以操作容器中的元素。
#include <iostream> #include <vector> #include <algorithm> #include <numeric> using namespace std; int main() { vector<int> vec = {5, 2, 8, 1, 9, 3}; // 排序 sort(vec.begin(), vec.end()); cout << "Sorted vector: "; for (int num : vec) { cout << num << " "; } cout << endl; // 查找元素 auto it = find(vec.begin(), vec.end(), 8); if (it != vec.end()) { cout << "Element 8 found at position: " << distance(vec.begin(), it) << endl; } else { cout << "Element 8 not found" << endl; } // 计算总和 int sum = accumulate(vec.begin(), vec.end(), 0); cout << "Sum of elements: " << sum << endl; // 反转容器 reverse(vec.begin(), vec.end()); cout << "Reversed vector: "; for (int num : vec) { cout << num << " "; } cout << endl; // 检查所有元素是否满足条件 bool allGreater = all_of(vec.begin(), vec.end(), [](int x) { return x > 0; }); cout << "All elements are greater than 0: " << (allGreater ? "Yes" : "No") << endl; return 0; }
五、高级泛型技术
5.1 模板元编程(TMP)
模板元编程是一种在编译时执行计算的技术,利用模板实例化机制进行编译时计算。
#include <iostream> using namespace std; // 编译时计算阶乘 template <int N> struct Factorial { static const int value = N * Factorial<N-1>::value; }; // 特化终止递归 template <> struct Factorial<0> { static const int value = 1; }; // 编译时判断是否为偶数 template <int N> struct IsEven { static const bool value = (N % 2 == 0); }; int main() { // 编译时计算5的阶乘 cout << "Factorial of 5: " << Factorial<5>::value << endl; // 编译时判断7是否为偶数 cout << "Is 7 even? " << (IsEven<7>::value ? "Yes" : "No") << endl; return 0; }
5.2 变参模板
变参模板允许模板接受任意数量和类型的参数。
#include <iostream> using namespace std; // 递归终止函数 void print() { cout << endl; } // 变参模板函数 template <typename T, typename... Args> void print(T first, Args... args) { cout << first; if constexpr (sizeof...(args) > 0) { cout << ", "; } print(args...); // 递归展开参数包 } // 计算参数的和 template <typename T> T sum(T value) { return value; } template <typename T, typename... Args> T sum(T first, Args... args) { return first + sum(args...); } int main() { print(1, 2.5, "hello", 'A'); cout << "Sum: " << sum(1, 2, 3, 4, 5) << endl; cout << "Sum: " << sum(1.5, 2.5, 3.5) << endl; return 0; }
5.3 类型 traits
类型 traits 是模板元编程的一种应用,用于在编译时获取类型信息。
#include <iostream> #include <type_traits> using namespace std; // 模板函数,根据类型特性执行不同操作 template <typename T> void process(T value) { if constexpr (is_integral_v<T>) { cout << "Processing integral type: " << value << endl; } else if constexpr (is_floating_point_v<T>) { cout << "Processing floating point type: " << value << endl; } else { cout << "Processing other type" << endl; } } int main() { process(42); // 整数类型 process(3.14); // 浮点类型 process("hello"); // 其他类型 // 使用type_traits判断类型关系 cout << "int is a base of double? " << is_base_of_v<int, double> << endl; cout << "vector<int> is a container? " << is_same_v<vector<int>::value_type, int> << endl; return 0; }
六、泛型编程的最佳实践
- 保持接口简单:泛型组件的接口应尽可能简单,减少使用者的负担
- 提供清晰的文档:说明模板参数的要求和约束
- 使用概念(C++20):约束模板参数的类型要求
- 测试多种类型:确保泛型代码在不同类型下都能正常工作
- 注意性能:避免不必要的拷贝和类型转换
- 考虑特化:对于特定类型的特殊需求,考虑使用模板特化
- 利用STL:尽可能使用现有的STL组件,避免重复造轮子
七、总结
泛型编程是C++强大的编程范式之一,通过模板机制可以创建高效、灵活且类型安全的代码。标准模板库(STL)进一步提供了丰富的容器、算法和迭代器,大大提高了开发效率。
掌握泛型编程技术需要理解模板的工作原理、STL的使用方法以及高级泛型技术如模板元编程和变参模板。通过合理应用泛型编程,可以编写出更加通用、复用性更强的代码。
- 1