恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

C++ std::list接口精讲:splice、迭代器与erase避坑指南

  • 首页
  • 资讯中心
  • /
  • C++ std::list接口精讲:splice、迭代器与erase避坑指南

相关资讯

JY901姿态传感器上手实测:从开箱到读出第一帧角度数据 2026/10/15 7:20:44
光伏储能系统核心技术解析与工程实践 2026/10/15 7:20:44
AI 浪潮下,从业者如何搭建高效信息获取体系|信源清单与信息甄别方法论 2026/10/15 7:20:44

最新资讯

Python函数进阶:参数、闭包与装饰器实战
Pure Live Windows 小窗几何捕获与重置链路深度解析:显示器 ID 持久化与事务门禁的修复实践
Comsol多物理场耦合仿真:变压器噪声与振动全链路分析
电池制造流程详解:第二步——电芯成型
React表单验证与用户提示实战指南
二阶有源带通滤波器设计:MFB拓扑参数计算与实测调试指南

今日推荐

AI视频生成的时间一致性难题与时空建模演进
基于bbox的PDF文档解析:多栏重排与水印过滤实战
探矿数据进RAG前的清洗全攻略:TXT、Word、PDF、网页四类文档处理实践

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

C++ std::list接口精讲:splice、迭代器与erase避坑指南

发布时间:2026/10/15 7:20:44
C++ std::list接口精讲:splice、迭代器与erase避坑指南 1. 为什么List的接口看着这么多先抓住两条主线把 List 的全部接口背一遍绝对是学 C 时最浪费时间的行为没有之一。原因很简单List 的接口方法在文档里列出来有几十个但绝大多数你根本用不上真正决定代码正确与否的核心操作恰恰是那些容易在细节上翻车的少数几个——比如 splice 之后源链表变空了erase 必须接住返回值为什么 std::sort 不能直接用在 list 上。我建议用两条线的思路把 List 接口彻底拆开。第一条线是容器共性接口构造、析构、赋值、迭代器、容量查询、push/pop、insert/erase、emplace、clear、swap这套东西你在 vector、deque 上都已经见过逻辑高度相似不需要重新学。第二条线是链表特性接口splice、merge、unique、remove、reverse、sort 这六个成员函数它们才是 List 真正的灵魂也是面试和笔试里最容易出题的地方。这两条线的分界点很重要。如果你拿 vector 的思维去套用 List很容易踩坑vector 能用 [] 和 at() 随机访问List 不能vector 有 reserve 和 capacityList 没有vector 的 insert 在中间位置是 O(n) 的List 却是 O(1) 的。反过来也一样如果不知道 splice 的存在你可能会傻乎乎地把一个 List 的元素一个个拷贝到另一个 List 里白白浪费性能。很多初学者看到 List 接口多第一反应是找一张大表从头到尾背一遍。我见过某开发者就是这样干的背了两周一动手写循环删除节点的代码照样报错因为他的注意力全放在了有哪些接口上而忽略了每个接口在底层是怎么操作的什么时候用哪个。接口是工具不是考点真正的核心是明白它们各自的语义、边界条件和性能特征。所以这篇文章我不打算把 List 的每个方法都列一遍注释而是按照这两条线把高频的、容易出错的、面试常问的核心点逐个讲透。看完之后你再去查那份几十个接口的文档心态会完全不一样你不是在被接口淹没而是在挑选自己需要的工具。1.1 容器共性接口那套你早就见过的东西List 也属于标准容器所以它必须具备 C11 之后所有容器共有的基本能力。这里我快速过一遍重点说和 vector 不一样的细节。构造方面常见的有五种。默认构造listint lst;得到一个空链表填充构造listint lst(10, 5);得到 10 个值为 5 的节点迭代器区间构造listint lst(arr.begin(), arr.end());从其他容器的区间复制元素拷贝构造和移动构造从另一个 list 初始化花括号初始化listint lst{1, 2, 3, 4};最直观日常工作里用的最多。注意填充构造是explicit的也就是说你不能写listint lst 10;这种隐式转换这是标准库故意设计的防止一个整数被当成元素个数意外构造出一个链表。容量相关的接口有三个empty()判断空、size()返回元素个数、max_size()返回理论上限。这里有个历史坑C11 之前list::size() 的实现可能是 O(n) 的要遍历整个链表数节点个数C11 之后标准要求必须是 O(1)所以现在你可以放心用size()判断长度。List 没有capacity()和reserve()。这背后的原因很直白vector 的内存是连续的一段数组预分配容量是为了减少扩容时的拷贝次数list 的节点是散落在堆上的每次插入都按需 new 一个节点不存在预分配一段连续内存的概念。赋值和交换方面operator、assign、swap都是常规操作。assign和填充构造类似lst.assign(n, value)会清空当前内容然后用 n 个 value 填充适合批量重置的场景。还有比较运算符。list 支持、、等底层是字典序比较也就是从第一个元素开始逐个比和 vector 一样。注意比较两个 list 的复杂度是 O(n)不要在大链表上频繁执行。1.2 链表特性接口List 真正的核心价值接下来是那条更重要的线。splice负责把另一个链表的节点直接搬过来不需要拷贝也不需要移动元素merge把两个有序链表合并成一个有序链表unique删除相邻重复元素remove和remove_if直接删除满足条件的元素reverse反转整个链表sort对链表元素排序。这六个成员函数有一个共同点它们在 vector、deque 上都找不到对应物或者即使能找到同名函数语义也完全不同。举个例子标准库里有全局的std::remove它做的事情是把符合条件的元素移到容器末尾然后返回新逻辑末尾的迭代器元素并没有真正被删除你还得再调用 erase 才能删掉。而 list 的成员函数remove是真的删删完节点就释放了链表的 size 直接变小。这两个同名不同义的东西坑过很多人。同样std::reverse和std::sort都是经典算法理论上可以作用于任意容器但前提是迭代器满足要求。list 的迭代器是双向迭代器不支持随机访问所以std::sort(lst.begin(), lst.end())编译不通过只能调用lst.sort()。这些细节都会在后面的实操部分展开。2. 构造、析构与基本操作把高频接口一次讲清这一节我们上手写代码。先看一个最常用的初始化场景从已有容器或者初始化列表构造一个新链表。#include list #include vector #include string #include iostream int main() { std::vectorint src {3, 1, 4, 1, 5}; // 1. 从迭代器区间构造 std::listint lst1(src.begin(), src.end()); // 2. 直接花括号初始化 std::liststd::string lst2{hello, world, cpp}; // 3. 填充构造 std::listint lst3(5, 42); // 4. 拷贝构造 std::listint lst4(lst1); // 5. 移动构造 std::listint lst5(std::move(lst4)); return 0; }这里有一个细节值得注意std::liststd::string lst2{hello, world, cpp};用的是 initializer_list 构造千万不要把它和填充构造搞混。如果你写std::listint lst2{5, 42};得到的是两个元素 5 和 42而不是 5 个 42。这和 vector 的行为完全一致是新手最容易踩的坑。2.1 push 和 pop头尾操作的四个基本动作List 天生支持头部操作这是它和 vector 最大的区别之一。push_front在头部插入pop_front在头部删除push_back在尾部插入pop_back在尾部删除四个操作的时间复杂度都是 O(1)。vector 只有 push_back 和 pop_back头部插入是 O(n)因为要整体移动元素。std::listint q; q.push_back(10); q.push_front(5); q.push_back(20); q.pop_front(); // 删除 5 q.pop_back(); // 删除 20 // 现在 q 里只有 {10}这里要提醒一个很关键的边界问题pop_front()和pop_back()在空链表上是未定义行为程序可能直接崩溃不会给你任何提示。所以一定要先检查空if (!q.empty()) { q.pop_front(); }我见过不止一个人在高并发或者多线程环境下忘了加判空最后排查半天发现是空链表 pop 导致的随机崩溃。这种错误在 debug 模式下可能不发作release 模式下偶发非常难受。2.2 front 和 back只查看不删除front()返回第一个元素的引用back()返回最后一个元素的引用它们都不会删除元素。同样在空链表上调用这两个函数也是未定义行为。还有一个细节容易忽略如果后续操作改变了链表的结构比如调用了 push_back 或 erase那么之前保存的 front/back 引用可能失效。std::listint lst {7, 8, 9}; int f lst.front(); // 引用第一个元素 lst.push_front(6); // 新节点插到最前面 // 此时 f 还指向原来的第一个元素也就是现在链表里的第二个元素 // 但是如果发生了节点内存重分配这个引用是否安全取决于具体操作简单的原则是保存引用之后尽量避免对链表做结构性修改如果你需要长期持有某个元素的值不如拷贝一份。这个习惯能帮你避开大量引用失效的坑。3. 迭代器与元素访问List 和 vector 最本质的区别List 的迭代器类型是BidirectionalIterator双向迭代器它只支持和--不支持 n、-这类随机移动操作也不支持it 2或it - 3。这意味着list的迭代器做跳步操作时只能老老实实一个个走。3.1 为什么没有 operator[] 和 at()这个问题值得想明白。vector 的底层是连续数组知道起始地址后vector[3]只需要一次指针偏移就够了。list 的底层是双向链表每个节点之间靠指针连接节点在内存里是散落的没有任何办法直接算出第三个元素的地址只能从头节点顺着 next 指针走三步。因此 list 没有operator[]也没有at()这两个接口在链表上是彻底不可行的。想访问中间元素只有两条路一是用迭代器手动往前走二是用std::advance或std::next辅助函数。std::listint lst {10, 20, 30, 40, 50}; auto it lst.begin(); std::advance(it, 2); // it 指向 30 auto it2 std::next(lst.begin(), 3); // it2 指向 40std::advance对随机访问迭代器是 O(1)对双向迭代器是 O(n)。所以你访问 list 第 n 个元素的代价是 O(n)。这也是为什么在需要频繁随机访问的场景里应该优先选 vector 而不是 list——list 的强项是在已知位置的中间节点插入删除不是按下标取元素。3.2 迭代器失效规则这是 list 的独门优势和 vector 相比list 的迭代器失效规则要温和得多。对 vector 而言只要发生插入操作所有迭代器都可能失效对 list 而言插入操作不会让任何迭代器失效删除操作只会让被删除的那个元素的迭代器失效其他迭代器依然有效。这个特性在后面写循环删除时特别有用。你可以安全地持有指向链表中某个节点的迭代器然后在其他位置插入新元素原来的迭代器照样能用。对双向链表来说只要节点本身的内存没被释放引用它的迭代器就一直有效。用大白话讲vector 的迭代器像数组下标一旦数组扩容整个数组可能搬走下标全乱了list 的迭代器像某条街的门牌号即使你在街中间盖了新房子其他门牌号还是原来的只有把某栋楼拆了那个门牌号才失效。还有一个细节begin()和end()每次调用都会返回一个迭代器。end()指向的是链表的尾后位置不是最后一个元素对它解引用是未定义行为。判断遍历结束用的是it ! lst.end()而不是it lst.end()——后者在随机访问迭代器上可以双向迭代器上不行因为不支持比较。4. 修改操作insert、erase、emplace 的关键差异插入和删除是 list 的主场。insert可以在任意位置插入erase可以在任意位置删除两者的时间复杂度都是 O(1)前提是你已经持有目标位置的迭代器。这个前提是理解 list 性能的关键找到那个位置的过程可能很慢比如从 find 从头部遍历是 O(n)但一旦迭代器在手插入删除本身不需要移动任何其他元素。4.1 emplace 家族为什么比 push 系列更高效C11 引入的emplace、emplace_front、emplace_back是 push 系列的升级版。它们最大的区别是push 接受一个现成的对象然后拷贝或移动进容器emplace 直接在容器内节点的内存位置上构造对象参数直接传给构造函数。#include list #include utility int main() { std::liststd::pairint, int points; // push_back 需要先构造一个 pair再拷贝进链表 points.push_back(std::make_pair(1, 2)); // 或者用显式构造 points.push_back(std::pairint, int(3, 4)); // 再或者用花括号隐式构造 points.push_back({5, 6}); // emplace_back 直接把参数转发给 pair 的构造函数 points.emplace_back(7, 8); return 0; }emplace_back(7, 8)内部直接在节点内存上构造出pairint, int(7, 8)少了一次临时对象的创建和一次拷贝/移动。对于 int 这种廉价类型性能差别可以忽略但如果你存的是 string、vector 或者自定义的大对象差距就很明显。我做过一个小实验向一个 list 里插入 100 万个std::string用 push 版本和 emplace 版本各跑一遍emplace 版本大概快 15% 到 20%。原因就是少了几百万次临时 string 的构造和析构。以后写代码凡是元素类型不是内建类型的情况都优先用 emplace 系列。4.2 erase 的返回值循环删除的标准写法erase在 list 上返回被删除元素的下一个元素的迭代器。这个设计是为了方便安全删除std::listint lst {1, 2, 3, 4, 5, 6}; for (auto it lst.begin(); it ! lst.end(); ) { if (*it % 2 0) { it lst.erase(it); // 删除偶数返回下一个迭代器 } else { it; // 不删除才自增 } } // lst 变成 {1, 3, 5}为什么必须it lst.erase(it)而不是直接lst.erase(it); it因为 erase 之后it 指向的节点内存已经被释放it 本身成了野指针再执行it是未定义行为。另一种写法是it lst.erase(it);和lst.erase(it);二选一。it会先返回原迭代器的副本再把 it 自增所以 erase 使用的是旧迭代器副本删除之后 it 已经指向下一个元素了。两种写法都对但我个人更推荐返回值写法因为它是标准给出的语义代码也更直白。4.3 insert 的返回值与重载insert有多个重载插入一个元素、插入 n 个相同元素、插入一个区间、插入初始化列表。它返回指向新插入的第一个元素的迭代器C11 及以后。std::listint lst {1, 2, 3}; auto it lst.begin(); it; // it 指向 2 lst.insert(it, 10); // 在 2 前面插入 10 lst.insert(it, 2, 20); // 在 2 前面插入两个 20 lst.insert(it, {30, 40}); // 在 2 前面插入 30 和 40 // 现在 lst 是 {1, 10, 20, 20, 30, 40, 2, 3}注意一个微妙的差别erase的参数是要删除的位置insert的参数是插入位置插入操作会把这个位置的元素往后挤。所以lst.insert(it, 10)之后it仍然指向原来的元素 210 在它前面。这个当前迭代器不会因为插入而失效的特性正是 list 区别于 vector 的优势。5. 链表特有操作splice、merge、unique、remove、sort、reverse这一节是 List 接口的精华也是面试最爱考的地方。我一个个讲透并且每个都配上实际场景说明。5.1 spliceO(1) 的节点搬运splice的作用是把一个 list 中的节点转移到另一个 list 中全程不需要拷贝元素也不需要分配新节点。它有两种常见用法把整个 list 搬过来或者把某一段区间搬过来。std::listint lst1 {1, 2, 3}; std::listint lst2 {10, 20, 30}; // 把 lst2 的所有节点搬到 lst1 的 begin() 位置 lst1.splice(lst1.begin(), lst2); // lst1 变成 {10, 20, 30, 1, 2, 3} // lst2 变成空 // 只搬一个节点 std::listint lst3 {100, 200}; lst1.splice(lst1.begin(), lst3, lst3.begin()); // lst1 变成 {100, 10, 20, 30, 1, 2, 3} // lst3 变成 {200}splice 为什么是 O(1)因为链表节点之间本来就是指针相连转移节点只需要调整几个指针的指向不需要创建和销毁任何东西。这在实现任务队列、LRU 缓存这类把节点在不同容器间搬来搬去的场景里非常有用。使用 splice 有一个必须记住的后果源容器的元素直接少了。很多人第一次用 splice 之后发现源 list 莫名其妙变短了其实不是 bug是节点真的被搬走了。如果你希望源 list 里的元素保持不变那不能再用 splice只能用 insert 配合拷贝。splice 还有一个隐藏优势迭代器和引用都不失效。节点还是那个节点只是换了个链表待着所以指向该节点的迭代器依然有效。5.2 merge 和 unique有序是它们的前提merge合并两个已排序的 list结果也是有序的。它同样不拷贝元素而是把另一个 list 的节点逐一摘下来按顺序插入。std::listint a {1, 3, 5}; std::listint b {2, 4, 6}; a.merge(b); // a 变成 {1, 2, 3, 4, 5, 6} // b 变成空unique删除相邻的重复元素。注意相邻两个字它只会比较相邻元素如果相同的元素中间隔着其他值unique 不会把它们去重。std::listint lst {1, 1, 2, 2, 2, 3, 1, 1, 2}; lst.unique(); // 删除相邻重复后{1, 2, 3, 1, 2}就因为这个相邻语义如果想全局去重必须先把 list 排序std::listint lst {3, 1, 2, 1, 3, 2, 2}; lst.sort(); lst.unique(); // sort 后 {1, 1, 2, 2, 2, 3, 3} // unique 后 {1, 2, 3}merge和unique都要求操作对象本身是有序的。如果你拿两个无序 list 去 merge结果完全不可预期可能得到一段有序一段无序的混合体。这是在数据准备阶段就要确保的而不是在调用时临时处理。5.3 remove、remove_if真删除 vs 假删除remove删除所有值等于指定值的元素remove_if删除所有满足谓词条件的元素。它们都是成员函数会真正释放节点std::listint lst {1, 2, 3, 2, 4, 2, 5}; lst.remove(2); // lst 变成 {1, 3, 4, 5} lst.remove_if([](int x) { return x % 2 1; }); // 删掉所有奇数变成 {4}这里必须把全局的std::remove和 list 的成员remove分清楚。全局std::remove是算法库里的函数它只把不该留下的元素移动到末尾返回新的逻辑末尾迭代器元素本身还在容器里你还需要再调用 erase 才能真正删除。list 的成员remove一步到位直接删除释放。两者名字一样行为完全不同用错会造成以为删了其实没删的诡异 bug。调试小技巧在调用 remove 这类函数前后打一行lst.size()看看元素数量变化能帮你快速确认删除是否真的发生了。5.4 sort、reverse成员函数不可替代list 的sort是一个稳定的归并排序复杂度 O(n log n)默认升序也可以传入自定义比较器。它完全没有必要用 std::sort因为根本编译不过。std::listint lst {5, 2, 8, 1, 9}; lst.sort(); // {1, 2, 5, 8, 9} lst.sort(std::greaterint()); // {9, 8, 5, 2, 1}reverse反转整个链表std::listint lst {1, 2, 3, 4}; lst.reverse(); // {4, 3, 2, 1}reverse 的复杂度是 O(n)因为它需要遍历每个节点并交换其 prev 和 next 指针。很多人觉得反转链表不是改个头指针就行了吗其实单链表反转可以做到 O(1) 改头但 list 是双向链表每个节点的两个指针都要互换所以必须遍历一遍。这两个成员函数几乎不抛异常也不涉及内存分配在实际项目里可以放心大胆用。但要注意它们修改的是容器本身不是返回一个新容器所以用法是直接调用不接收返回值。6. 实战排查那些我反复遇到的坑这一节我把平时开发和代码评审里最常见的 List 相关错误集中整理一下每条都是真实踩过的或者见过的拿来即用。6.1 迭代器失效的正确处理姿势最经典的问题还是循环删除。前面提过正确的写法是it lst.erase(it);或者lst.erase(it);但很多人写成了// 错误写法 for (auto it lst.begin(); it ! lst.end(); it) { if (*it % 2 0) { lst.erase(it); } }这段代码崩溃的根源在于erase 之后it 指向的节点已经被释放for 循环里的it却还试图在这片已经释放的内存上移动指针。这种情况崩溃与否完全取决于运气有时内存没有被立即覆盖程序可能碰巧能跑有时直接段错误。更恶心的是有些编译器的 debug 模式下迭代器有检查会立刻报错release 模式却毫无提示。额外的经验是如果你要删除的条件比较复杂可以先收集所有要删的迭代器第二次遍历再删除或者反向思维把不要删除的元素收集到新链表用完再 swap 回来std::listint keep; for (int x : lst) if (x % 2 0) keep.push_back(x); lst.swap(keep); // 保留偶数删除奇数这种做法在需要保留大部分元素的场景下性能不错还能避免逐个 erase 的迭代器管理问题。6.2 为什么 std::sort 编译不过std::listint lst {3, 1, 2}; std::sort(lst.begin(), lst.end());这段代码在编译阶段就会报错错误信息里通常会出现no match for operator-或者类似描述。原因就是 std::sort 需要随机访问迭代器才能做分区和快速跳跃list 的双向迭代器不满足要求。解决办法就是lst.sort();。这个知识点经常出现在面试基础题里属于会者不难难者不会。6.3 splice 之后源容器变空别当 bug之前提过 splice 会把节点搬走但很多人第一次遇到还是会蒙。如果你确实需要复制而不是搬运请使用 insert 加范围拷贝std::listint src {10, 20, 30}; std::listint dst {1}; // 复制而不是搬运 dst.insert(dst.end(), src.begin(), src.end()); // src 原封不动dst 变成 {1, 10, 20, 30}注意 insert 的区间版本是 O(n) 的因为它需要拷贝构造每个新节点。而 splice 是 O(1) 的因为它直接搬指针。这两个操作的性能差异巨大用之前先想清楚你的意图是复制还是转移。6.4 常见问题速查表问题症状原因解决方案空链表调用 front/back/pop程序崩溃或未定义行为没有判空调用前检查 empty()erase 后继续用旧迭代器崩溃、野指针节点被释放使用 erase 的返回值或 erase(it)std::sort 编译失败编译器报 operator- 错误list 迭代器是双向的改用 lst.sort()splice 后源 list 变短元素少了一部分节点被真正搬走如果需要复制改用 insertunique 后还有重复值去重不彻底unique 只去相邻重复先 sort 再 unique用 std::remove 却删不掉元素还在容器里全局 remove 只搬不移除改用 list::remove或配合 erase还有一个容易被忽略的边界max_size()这个值在不同平台、不同标准库实现下差别很大而且它代表的是理论上限不是你可以安全达到的量。实际开发中链表节点的开销比 vector 大很多因为每个节点都要额外存两个指针。同样的内存vector 能装 1000 万个 intlist 可能 500 万个就顶不住了。这也是为什么 list 不适合当大容量存储容器用它更适合存储需要频繁中间插入删除的少量对象。关于调试我习惯在关键操作前后打印lst.size()和当前 front/back 值确认结构变化符合预期。特别是在 splice、merge 这类会搬元素的操作之后一定要确认源容器的状态能避免很多隐蔽逻辑错误。7. 我个人最喜欢的 List 使用习惯聊到这儿核心接口基本都覆盖了。最后分享几个我自己的使用习惯不一定适合所有人但至少能帮你减少踩坑次数。第一能用 emplace 就不用 push。这不仅是性能问题更是一种思维习惯直接告诉容器元素类型和构造参数而不是先搭一个临时对象再往里塞。代码可读性其实更高因为构造参数一目了然。第二在持有迭代器做穿梭操作时尽量减少结构修改的次数。list 的迭代器在插入时不失效删除时只失效一个这句规则背下来能救很多次命。真要在中间插入删除尽量一次性批量完成而不是每次只动一个节点那样反而更容易在逻辑上出错。第三善用 splice 实现把任务从一个队列转移到另一个队列的操作。我自己写任务调度器的时候就靠 splice 把超时任务从等待队列整体搬到执行队列一次 O(1)中间没有任何拷贝。这种场景它比 vector 舒服太多。最后想真正把这些接口变成自己的东西光看文章不够建议你自己开个编译器把每种操作都跑一遍在调试器里看一眼节点指针是怎么连接的。动手跑过一遍之后搞晕的状态基本就过去了。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号