所有分类
  • 所有分类
  • 游戏源码
  • 网站源码
  • 单机游戏
  • 游戏素材
  • 搭建教程
  • 精品工具

三十分钟掌握STL教程|STL入门必看快速上手实用指南

三十分钟掌握STL教程|STL入门必看快速上手实用指南 一

文章目录CloseOpen

我当初学STL的时候也这样——盯着“模板元编程”“迭代器分类”这些概念看了半天,越看越懵。后来跟着一位做了10年C++开发的前辈摸出个“笨办法”:不管复杂理论,先抓最常用的功能练手,30分钟就能把STL从“书本知识”变成“能用的工具”。今天就把这套方法分享给你,没基础也能跟着做,亲测有效。

先搞定3个高频容器:用最笨的方式记住“怎么用”

STL的核心是“容器+算法+迭代器”,但对新手来说,容器是最该先抓的——因为你写代码90%的时间都在和容器打交道。别贪多,先记3个最常用的:vector(动态数组)、map(键值对字典)、set(自动排序集合)。

我之前写过一个学生信息管理的小项目,一开始用普通数组存数据,添加学生要手动扩容,删除学生要挨个挪元素,代码写得又长又容易错。后来换成vector,直接用push_back()加数据,erase()删数据,瞬间轻松了——你看,这就是容器的意义:帮你“偷懒”,把底层细节藏起来。

vector:先学会“动态数组”的3个核心操作

vector是新手最该先练的容器,因为它的用法最接近普通数组,场景也最广——比如存列表数据、顺序存储元素,都能用它。

别记那些花里胡哨的构造函数,先学会最常用的两种初始化:要么空初始化然后push_back(),比如vector names; names.push_back("Alice");;要么直接用花括号装初始值,比如vector scores = {90, 85, 95};

遍历的时候别用复杂的迭代器语法,直接用range-based for循环(就是for(auto x 容器)),简单又不容易错。我之前用迭代器遍历的时候,总忘写begin()end(),或者不小心写成it++导致越界,换成range-based for之后,再也没犯过这错。

还有删除元素,别用erase()的时候直接丢迭代器——我之前就犯过傻:遍历vector的时候用erase(it),结果迭代器失效,程序直接崩溃。后来前辈告诉我,erase()会返回下一个有效的迭代器,所以正确写法是it = nums.erase(it),比如删除所有偶数:

vector nums = {1,2,3,4,5};

for(auto it = nums.begin(); it != nums.end(); ) {

if(it % 2 == 0) {

it = nums.erase(it); // 用返回值更新迭代器

} else {

++it;

}

}

map:用“字典”思维解决查询问题

map是“键值对容器”,像一本字典——你用“键”(比如订单号、用户ID)就能快速找到“值”(比如订单信息、用户资料)。我之前做电商订单查询功能的时候,一开始用vector存订单,要找某个订单得遍历整个数组,数据多的时候卡得要命。后来换成map,直接用orders["Order123"]就能拿到信息,查询速度快了10倍都不止。

map的核心用法就两个:插入查询。插入可以用insert(),也可以直接用[key] = value——比如存用户电话:map phoneBook; phoneBook["Alice"] = "123456";。查询的时候直接用[key],或者用find()函数(更安全,因为如果key不存在,[key]会自动插入一个默认值)。

提醒一句:map的键是唯一的——要是插入重复的键,会覆盖原来的值。我之前就犯过这错:把同一个订单号插入两次,结果后面的覆盖了前面的,排查了半小时才找到问题。所以如果需要存多个相同键的值,得用multimap,但新手先把map用熟再说。

set:自动排序+去重,帮你省掉排序代码

set是“自动排序的集合”,适合两种场景:去重或者快速找最大/最小值。比如你想统计一组数据里的不同值,直接把数据插进set,自动就去重了;或者想找最大的数,直接取set.rbegin()(反向迭代器的第一个元素)。

我之前做成绩统计的时候,需要找出最高分和最低分,一开始用vector存成绩,然后自己写排序函数,结果要么排序错了,要么漏了边界条件。后来用set,插入所有成绩后,set.begin()是最低分,set.rbegin()是最高分,直接搞定,代码量少了三分之一。

注意:set里的元素不能修改——因为修改会破坏排序结构。要是你想改元素值,得先删了旧的,再插新的,比如set.erase(oldValue); set.insert(newValue);

必练2个核心算法:让STL帮你“偷懒”

STL的算法库有几百个,但新手不用都学,先抓最常用的两个sort()(排序)和find()(查找)——这两个算法能解决80%的日常问题。

sort():比你写的冒泡排序高效100倍

别自己写排序算法了,STL的sort()比你写的高效得多——它用的是“快速排序+插入排序”的混合算法,时间复杂度是O(n log n),而且稳定性好。

sort()的核心是传对范围:比如排序vector,直接传begin()end(),比如sort(nums.begin(), nums.end());;要是想降序排,加个greater(),比如sort(nums.begin(), nums.end(), greater());

我之前写一个成绩排序的功能,自己写了个冒泡排序,数据多的时候要等好几秒,换成sort()之后,瞬间就出结果了。更方便的是,sort()还能自定义排序规则——比如按字符串长度排序,只要写个lambda表达式就行:

vector words = {"apple", "banana", "cherry"};

sort(words.begin(), words.end(), [](const string& a, const string& b) {

return a.size() < b.size(); // 按长度升序排

});

find():别手动遍历了,让算法帮你找

找元素的时候别用for循环遍历——尤其是数据多的时候,又慢又容易错。用find()函数,直接传范围和目标值,比如找vector里的“3”:

auto it = find(nums.begin(), nums.end(), 3);

if(it != nums.end()) {

cout << "找到了:" << *it << endl;

} else {

cout << "没找到" << endl;

}

我之前找数组里的某个值,手动写for循环的时候,总忘写i < nums.size(),导致数组越界报错。换成find()之后,再也没出过这问题——因为find()会自动处理边界,找不到就返回end()

最后:用一张表把核心内容“刻”在脑子里

为了帮你快速记住这些内容,我整理了一张高频容器&算法的核心用法表,你可以存下来随时看:

类型 核心场景 常用操作 注意事项
vector 动态数组、顺序存储 push_back()添加、erase()删除、[]访问 erase()要拿返回值更新迭代器
map 键值对查询、字典场景 [key]访问、insert()插入、find()查找 键唯一,重复插入会覆盖
set 自动排序、去重 insert()添加、erase()删除、begin()取最小 不能修改元素,否则破坏排序
sort() 排序任意可迭代容器 传begin()/end()、自定义比较函数 默认升序,降序用greater()
find() 查找元素位置 传范围和目标值、判断是否等于end() 找不到返回end()

其实STL没你想的那么复杂——先抓最常用的功能练手,用得多了自然就懂那些理论了。你可以今天就试一下:比如用vector存几个整数,排序之后遍历输出;或者用map存几个朋友的姓名和电话,然后根据姓名查找。要是按这些方法试了,欢迎回来告诉我效果!要是遇到问题,也可以留言,我帮你看看。


我刚学vector的时候,最常踩的坑就是用erase()删元素——那时候哪懂什么迭代器失效啊,就觉得“删个元素而已,直接调用erase(it)不就完了”。结果有次写遍历删偶数的代码,写了个for循环:“auto it = nums.begin(); it != nums.end(); it++”,里面判断如果it是偶数,就直接nums.erase(it)——结果程序一跑就崩,报错说“迭代器不可解引用”。我盯着代码看了半小时,明明逻辑没错啊?后来问了公司里的老程序员才明白:erase()删掉当前元素的瞬间,那个位置后面的所有元素都会往前“挤”一位,原来的it指向的地方已经不是原来的元素了——比如你删了第3个元素,第4、5个元素会变成第3、4个,可it还指着原来的第3位,那地方现在可能是空的,或者根本不属于这个vector了,这时候再用it++或者解引用,能不出错吗?

后来我才知道,erase()其实藏了个“小心机”——它会返回下一个有效的迭代器。比如刚才的例子,正确的写法应该是把erase()的返回值赋给it,比如“it = nums.erase(it)”。你想啊,删完元素后,it直接被更新成下一个有效的位置,就不用怕指着无效的地方了。而且遍历的时候,for循环里不能再写it++了,得把“加一”放到else里——比如“if (it % 2 == 0) { it = nums.erase(it); } else { ++it; }”。为啥?因为如果删了元素,it已经被erase()推进到下一个位置了,再自己加一就会跳过元素;没删的话,才需要手动把it往前挪一位。我后来用这个方法改了代码,果然再也没出现过迭代器失效的问题——甚至连原来写代码时的“怕错心理”都少了,因为知道这么写肯定稳。

其实现在回头想,迭代器失效的问题,本质就是“你以为it还指着原来的位置,但vector已经偷偷变了”。erase()的返回值就是帮你“跟上”vector的变化——它告诉你“删完之后,下一个该处理的元素在这”。我后来教新手的时候,都会先让他们记这个“笨办法”:只要用erase(),就把返回值赋给迭代器,准没错。毕竟对新手来说,先解决“能用”的问题,再理解“为什么”,比盯着理论啃管用多了。


新手学STL,优先选哪几个容器练手?

先抓3个高频容器:vector(动态数组,用法接近普通数组,适合顺序存储)、map(键值对字典,适合根据键快速查询)、set(自动排序集合,适合去重或找极值)。这三个容器覆盖了80%的日常开发场景,用熟后再扩展其他容器。

vector的erase()为什么会让迭代器失效?

因为erase()删除元素后,该位置后的元素会往前移动,原来的迭代器指向的位置就“无效”了。解决办法是用erase()的返回值——它会返回下一个有效的迭代器,比如写“it = vector.erase(it)”就能避免失效。

map和set的核心区别是什么?

map是“键值对容器”(存key-value),键唯一且对应一个值,适合比如“用户ID-用户信息”的查询场景;set是“单一元素集合”(只存value),元素自动排序且唯一,适合去重或快速找最大/最小值(比如成绩统计)。

STL算法必须和容器一起用吗?

是的,STL算法依赖“迭代器”连接容器——算法通过迭代器访问容器里的元素,不直接操作容器本身。比如sort()算法需要接收容器的begin()和end()迭代器,才能对容器内的元素排序。

学STL需要先懂模板元编程吗?

不需要。新手可以先跳过复杂理论(比如模板元、迭代器分类),先抓“常用功能”练手(比如vector的push_back()、map的find()),等用熟了容器和算法,再回头理解理论会更轻松。

原文链接:https://www.mayiym.com/46193.html,转载请注明出处。
0
显示验证码
没有账号?注册  忘记密码?

社交账号快速登录

微信扫一扫关注
如已关注,请回复“登录”二字获取验证码