【C转C++】 C转C++超值且好用的容器与函数
前言:欢迎各位光临本博客,这里小编带你直接手撕**,文章并不复杂,愿诸君**耐其心性,忘却杂尘,道有所长!!!!
《C语言》
《C++深度学习》
《Linux》
《数据结构》
《数学建模》
文章目录
- C转C++
- 一、map:键值对管理神器
- 1. 核心特性:自动排序的键值对
- 2. 底层逻辑
- 3. unordered_map:无序但更快的键值对
- 4. 常用操作:比C手动实现简单10倍
- 二、vector:动态数组
- 1. 核心特性:自动扩容的动态数组
- 2. 对比C数组
- 3. 常用操作
- 三、set:自动去重与排序
- 1. 核心特性:去重+排序
- 2. 对比C的实现
- 四、string:告别char*的字符串处理
- 1. 核心特性:像用int一样用字符串
- 2. 输入输出:解决cin的空格问题
- 3. 常用函数:字符串操作不用愁
- 4. 对比C的str系列函数:安全+简洁
- 五、其他常用工具:栈、哈希检查等
- 1. 栈(stack):先进后出的操作
- 2. contains:检查键是否存在(C++20+)
- 总结:C转C++,从这些容器开始
C转C++
从C转向C++,最直观的优势就是STL(标准模板库)带来的便捷——不用再手动管理内存、实现排序和查找,一行代码就能搞定C中需要几十行才能完成的功能。本文就来推荐几个C转C++必学的容器和函数,从键值对管理到动态数组,从集合操作到字符串处理,带你快速上手高效编程。
一、map:键值对管理神器
在C中,要实现“一个元素对应另一个元素”(比如“名字-分数”“ID-信息”),通常需要手动定义结构体+数组,还要自己写查找和排序逻辑,麻烦又容易出错。而C++的map
容器天生为键值对设计,自动排序+快速查找,直接省去大量重复工作。
1. 核心特性:自动排序的键值对
map
的本质是“关联容器”,每个元素是一个key-value
(键值对),且会自动按照键(key)从小到大排序。比如用字符串作为键时,会按字典序排序;用数字作为键时,会按数值大小排序。
#include <map> // 必须包含的头文件
#include <iostream>
using namespace std;int main() {// 定义一个map:键是string类型,值是int类型(比如“名字-分数”)map<string, int> score_map;// 1. 添加键值对(像用数组一样简单)score_map["Alice"] = 95; // 键“Alice”对应值95score_map["Bob"] = 88; // 键“Bob”对应值88score_map["Cindy"] = 92; // 键“Cindy”对应值92// 2. 访问值(直接通过键获取)cout << "Bob的分数:" << score_map["Bob"] << endl; // 输出:88// 3. 遍历所有键值对(自动按键排序)cout << "所有分数(按名字排序):" << endl;// 迭代器:类似指针,指向map中的每个键值对(结构体)for (auto it = score_map.begin(); it != score_map.end(); ++it) {// it->first 是键,it->second 是值cout << it->first << ":" << it->second << endl;}// 4. 获取键值对数量cout << "总人数:" << score_map.size() << endl; // 输出:3return 0;
}
运行结果:
Bob的分数:88
所有分数(按名字排序):
Alice:95
Bob:88
Cindy:92
总人数:3
可以看到,map
自动按名字的字典序(Alice→Bob→Cindy)排序,省去了手动调用qsort
的麻烦;访问时直接用map[key]
,比C中遍历数组查找高效10倍。
2. 底层逻辑
map
中的每个元素本质上是一个匿名结构体,类似我们在C中定义的:
// C中模拟键值对的结构体
struct KeyValue {char key[20]; // 键(名字)int value; // 值(分数)
};
struct KeyValue score_arr[100]; // 用数组存储
但map
帮我们做了三件事:
- 自动维护排序(无需手动调用排序函数);
- 自动扩容(无需担心数组越界);
- 快速查找(底层是红黑树,查找复杂度O(log n),比C中遍历数组的O(n)快得多)。
3. unordered_map:无序但更快的键值对
如果不需要键值对排序,unordered_map
是更好的选择——它底层是哈希表,查找、插入、删除的速度更快(平均O(1)),用法和map
几乎一样:
#include <unordered_map> // 头文件不同
#include <iostream>
using namespace std;int main() {unordered_map<string, int> score_umap;score_umap["Alice"] = 95;score_umap["Bob"] = 88;// 遍历:无序(按哈希表存储顺序)for (auto it = score_umap.begin(); it != score_umap.end(); ++it) {cout << it->first << ":" << it->second << endl;}return 0;
}
什么时候用哪个?
- 需要排序:用
map
(比如排行榜、字典); - 追求速度,无需排序:用
unordered_map
(比如缓存、索引)。
4. 常用操作:比C手动实现简单10倍
操作 | C++ map/unordered_map | C中手动实现 |
---|---|---|
插入 | map[key] = value 或 map.insert({key, value}) | 遍历数组找空位,手动赋值 |
查找 | map.find(key) != map.end() (存在) | 遍历数组对比每个key |
删除 | map.erase(key) | 遍历找到后移动元素覆盖 |
判空 | map.empty() | 检查数组长度是否为0 |
二、vector:动态数组
C中的数组堪称“新手噩梦”:静态数组大小固定(int arr[10]
),动态数组需要malloc
+realloc
+free
,稍不注意就内存泄漏或越界。而vector
作为“动态数组”,自动管理内存,大小随元素增减而变化,彻底解决这些问题。
1. 核心特性:自动扩容的动态数组
vector
可以理解为“会自动长大的数组”,初始化时可以指定大小,也可以空着,后续用push_back
随时添加元素,不用担心越界。
#include <vector> // 必须包含的头文件
#include <iostream>
using namespace std;int main() {// 1. 三种初始化方式vector<int> v1; // 空数组(大小0)vector<int> v2(5); // 大小为5的数组,默认值0vector<int> v3(3, 10); // 大小为3的数组,元素都是10(10,10,10)// 2. 动态添加元素(从末尾加,自动扩容)v1.push_back(20); // v1: [20]v1.push_back(30); // v1: [20,30]v1.push_back(40); // v1: [20,30,40]// 3. 重新设置大小(resize)v2.resize(3); // 原来大小5,现在改为3(只保留前3个元素,默认0→[0,0,0])// 4. 访问元素(和数组一样用[])cout << "v3[1] = " << v3[1] << endl; // 输出:10cout << "v1[2] = " << v1[2] << endl; // 输出:40// 5. 遍历元素(两种方式)// 方式1:下标遍历(和C数组一样)cout << "v1元素(下标遍历):";for (int i = 0; i < v1.size(); ++i) {cout << v1[i] << " ";}cout << endl;// 方式2:迭代器遍历(更通用,所有容器都支持)cout << "v3元素(迭代器遍历):";for (auto it = v3.begin(); it != v3.end(); ++it) {cout << *it << " "; // 迭代器解引用获取元素}cout << endl;// 6. 获取大小cout << "v1大小:" << v1.size() << endl; // 输出:3return 0;
}
运行结果:
v3[1] = 10
v1[2] = 40
v1元素(下标遍历):20 30 40
v3元素(迭代器遍历):10 10 10
v1大小:3
2. 对比C数组
问题 | C数组 | C++ vector |
---|---|---|
大小固定 | 静态数组int a[10] 不能改大小,超界危险 | 自动扩容,push_back 随便加 |
内存管理 | 动态数组需malloc(10*sizeof(int)) ,用完必须free ,易泄漏 | 自动申请和释放内存,出作用域自动销毁 |
扩容麻烦 | 需手动realloc ,还可能丢失数据 | 调用resize 或push_back 自动处理 |
传递参数 | 需同时传指针和长度(void func(int* a, int len) ) | 直接传vector ,内部带size() |
3. 常用操作
- 清空元素:
v.clear()
(替代C中手动遍历赋值或free
后重新malloc
); - 删除末尾元素:
v.pop_back()
(替代C中len--
,无需手动释放内存); - 判断是否为空:
v.empty()
(替代C中if (len == 0)
); - 获取首/尾元素:
v.front()
/v.back()
(替代C中a[0]
/a[len-1]
,更直观)。
三、set:自动去重与排序
在C中,要实现“不重复的元素集合”(比如去重后的ID列表),需要先存数组,再手动遍历去重,最后调用qsort
排序,步骤繁琐。而set
容器天生具备“自动去重”和“自动排序”功能,插入即处理完成。
1. 核心特性:去重+排序
set
中的元素不允许重复,且会自动从小到大排序,适合存储需要去重且有序的数据。
#include <set> // 必须包含的头文件
#include <iostream>
using namespace std;int main() {// 定义一个存储int的setset<int> id_set;// 1. 插入元素(自动去重+排序)id_set.insert(30);id_set.insert(10);id_set.insert(20);id_set.insert(10); // 重复元素,插入无效// 2. 遍历元素(自动排序)cout << "去重并排序后的ID:";for (auto it = id_set.begin(); it != id_set.end(); ++it) {cout << *it << " ";}cout << endl; // 输出:10 20 30 // 3. 查找元素(返回迭代器,找不到则等于end())int target = 20;auto it = id_set.find(target);if (it != id_set.end()) {cout << "找到元素:" << *it << endl; // 输出:找到元素:20} else {cout << "未找到元素:" << target << endl;}// 4. 删除元素id_set.erase(10); // 删除元素10cout << "删除10后的元素:";for (auto num : id_set) { // 简化的范围for循环(C++11+)cout << num << " ";}cout << endl; // 输出:20 30 return 0;
}
运行结果:
去重并排序后的ID:10 20 30
找到元素:20
删除10后的元素:20 30
2. 对比C的实现
比如实现“输入5个数字,去重后排序输出”:
- C语言需要:定义数组→输入→双重循环去重→调用
qsort
→输出(至少20行代码); - C++用
set
:插入5个数字→遍历输出(不到10行代码)。
// C语言实现去重排序
#include <stdio.h>
#include <stdlib.h>int cmp(const void* a, const void* b) {return *(int*)a - *(int*)b;
}int main() {int arr[5], temp[5];int len = 0;// 输入for (int i = 0; i < 5; ++i) {scanf("%d", &arr[i]);}// 去重for (int i = 0; i < 5; ++i) {int flag = 1;for (int j = 0; j < len; ++j) {if (arr[i] == temp[j]) {flag = 0;break;}}if (flag) {temp[len++] = arr[i];}}// 排序qsort(temp, len, sizeof(int), cmp);// 输出for (int i = 0; i < len; ++i) {printf("%d ", temp[i]);}return 0;
}
// C++ set实现(简洁10倍)
#include <set>
#include <iostream>
using namespace std;int main() {set<int> s;int num;// 输入并插入set(自动去重)for (int i = 0; i < 5; ++i) {cin >> num;s.insert(num);}// 遍历输出(自动排序)for (int n : s) {cout << n << " ";}return 0;
}
四、string:告别char*的字符串处理
C中的char*
字符串堪称“bug重灾区”:strcat
可能缓冲区溢出,strlen
需要遍历计算长度,拼接、截取字符串要手动分配内存……而C++的string
类把这些操作全部封装,安全又便捷。
1. 核心特性:像用int一样用字符串
string
可以直接赋值、拼接、比较,完全不用关心底层字符数组的内存管理。
#include <string> // 必须包含的头文件
#include <iostream>
using namespace std;int main() {// 1. 定义和赋值string s1 = "hello";string s2("world");string s3; // 空字符串// 2. 拼接(直接用+)string s4 = s1 + " " + s2; // s4 = "hello world"cout << "拼接结果:" << s4 << endl;// 3. 比较(直接用==、>、<)if (s1 == "hello") {cout << "s1等于\"hello\"" << endl;}if (s1 < s2) { // 按字典序比较(h < w)cout << "s1小于s2" << endl;}// 4. 长度(直接用length()或size())cout << "s4的长度:" << s4.length() << endl; // 输出:11(包括空格)return 0;
}
运行结果:
拼接结果:hello world
s1等于"hello"
s1小于s2
s4的长度:11
2. 输入输出:解决cin的空格问题
C中scanf("%s", str)
和C++中cin >> s
都只能读取“不含空格的单词”,遇到空格就停止。getline(cin, s)
可以读取一整行(包括空格),完美解决这个问题。
#include <string>
#include <iostream>
using namespace std;int main() {string s1, s2;cout << "输入一个单词(cin):";cin >> s1; // 输入“hello world”,只读取“hello”cout << "s1 = " << s1 << endl;// 注意:cin读取后,缓冲区会残留换行符,需要清空cin.ignore(); // 清空缓冲区的换行符cout << "输入一行文字(getline):";getline(cin, s2); // 输入“hello world”,完整读取cout << "s2 = " << s2 << endl;return 0;
}
运行结果:
输入一个单词(cin):hello world
s1 = hello
输入一行文字(getline):hello world
s2 = hello world
3. 常用函数:字符串操作不用愁
-
截取子串:
substr(n, m)
——从下标n
开始,截取m
个字符;substr(n)
——从下标n
截取到末尾(注意:字符串下标从0开始)。string s = "abcdef"; cout << s.substr(1, 3) << endl; // 从下标1取3个→"bcd" cout << s.substr(2) << endl; // 从下标2取到末尾→"cdef"
-
类型转换:
to_string(x)
——把数字(int、long等)转换成string。int num = 123; string s = to_string(num); // s = "123" long ln = 456789; string s2 = to_string(ln); // s2 = "456789"
-
查找字符/子串:
find(str)
——返回子串第一次出现的下标,没找到返回string::npos
。string s = "hello world"; size_t pos = s.find("world"); if (pos != string::npos) {cout << "找到子串,下标:" << pos << endl; // 输出:6 }
4. 对比C的str系列函数:安全+简洁
操作 | C(str系列函数) | C++ string |
---|---|---|
拼接 | strcat(str1, str2) (可能溢出) | s1 + s2 (自动扩容,安全) |
长度 | strlen(str) (遍历计算) | s.length() (直接返回,O(1)) |
比较 | strcmp(str1, str2) (返回0/1/-1) | s1 == s2 /s1 > s2 (直观) |
截取 | 手动strncpy +\0 (麻烦) | s.substr(n, m) (一行搞定) |
五、其他常用工具:栈、哈希检查等
除了上述容器,C++还有一些实用工具,能进一步简化代码。
1. 栈(stack):先进后出的操作
stack
封装了栈的基本操作(push
入栈、pop
出栈、top
取栈顶),比C中用数组模拟栈更安全(自动检查空栈,避免越界)。
#include <stack>
#include <iostream>
using namespace std;int main() {stack<int> st;// 入栈st.push(10);st.push(20);st.push(30);// 取栈顶(不删除)cout << "栈顶元素:" << st.top() << endl; // 输出:30// 出栈(删除栈顶)st.pop();cout << "出栈后栈顶:" << st.top() << endl; // 输出:20// 判空while (!st.empty()) { // 栈非空时循环cout << st.top() << " ";st.pop();} // 输出:20 10 return 0;
}
2. contains:检查键是否存在(C++20+)
map
和unordered_map
在C++20后支持contains(key)
函数,直接判断键是否存在,比find
更直观。
#include <map>
#include <iostream>
using namespace std;int main() {map<string, int> m{{"a", 1}, {"b", 2}};if (m.contains("a")) { // 检查是否有键"a"cout << "存在键\"a\"" << endl;}return 0;
}
总结:C转C++,从这些容器开始
从C转向C++,不需要一次性学完所有内容,先掌握map
(键值对)、vector
(动态数组)、set
(集合)、string
(字符串)这几个核心容器,就能解决80%的日常编程问题。它们的优势在于:
- 少写重复代码:排序、去重、内存管理交给STL;
- 更安全:避免缓冲区溢出、内存泄漏等C中常见bug;
- 更直观:用
+
拼接字符串、用[]
访问键值对,符合人类思维。