当前位置: 首页 > news >正文

48 C++ STL模板库17-容器9-关联容器-映射(map)多重映射(multimap)

STL模板库-容器9-关联容器-映射(map)多重映射(multimap)

文章目录

  • STL模板库-容器9-关联容器-映射(map)多重映射(multimap)
    • 一、映射(map)多重集映射(multimap)的特点
    • 二、共有函数(map 和 multimap 均支持)
      • 1. 构造函数与赋值操作
        • 1. 函数说明
        • 2. 用法示例
        • 3. 详细说明
      • 2. 迭代器操作
        • 1. 函数说明
        • 2. 用法示例
        • 3. 详细说明
      • 3. 容量查询
        • 1. 函数说明
        • 2. 用法示例
        • 3. 详细说明
      • 4. 修改器(基础操作)
        • 1. 函数说明
        • 2. 用法示例
        • 3. 详细说明
      • 5. 查找操作
        • 1. 函数说明
        • 2. 用法示例
        • 3. 详细说明
      • 6. 观察器
        • 1. 函数说明
        • 2. 用法示例
        • 3. 详细说明
      • 7. 非成员函数
        • 1. 函数说明
        • 2. 用法示例
        • 3. 详细说明
    • 三、map 和 multimap 功能差异对比表
        • 1. 函数说明
        • 2. 用法示例
        • 3. 详细说明
    • 四、map 独有函数
        • 1. 函数说明
        • 2. 用法示例
        • 3. 详细说明
    • 五、map 和 multimap 关键差异说明
    • 六、完整用法示例

映射(map)多重集映射(multimap) 是STL模板库里面的一种关联容器。存储键值对,键唯一,值可重复。
映射包含了多重映射的全部操作,元素以键值对有序排列(默认升序),映射的元素键不能重复,多重映射的元素键可以重复,两者使用方法相同,所有操作定义在头文件 <map> 中。

一、映射(map)多重集映射(multimap)的特点

  1. 有序关联容器

    • 存储键值对(pair<const Key, T>),键自动排序
    • 底层实现:基于红黑树(平衡二叉搜索树),保证元素始终按键排序(默认升序,可自定义比较函数)。
    • 时间复杂度:插入、删除、查找操作均为 O(log n),适合动态数据管理。
  2. 键值对存储

    • 元素类型为 pair<const Key, T>,里面每个元素都是成对的,前面的Key称之为“键”不可修改,不能重复。后面的(T)称之为“值”可修改(map 允许通过迭代器直接修改值)。

二、共有函数(map 和 multimap 均支持)

1. 构造函数与赋值操作

1. 函数说明
函数签名功能说明
map()/multimap()默认构造函数,创建空容器
map(InputIt first, InputIt last)通过迭代器范围构造
map(std::initializer_list<value_type>)初始化列表构造 (C++11)
operator=(const map&)拷贝赋值
operator=(map&&)移动赋值 (C++11)
operator=(std::initializer_list<value_type>)初始化列表赋值 (C++11)
~map()/~multimap()析构函数
2. 用法示例
  1. 默认构造(空容器)

    std::map<std::string, int> map1;          // 空map 
    std::multimap<int, double> multimap1;     // 空multimap 
    // ✅ 特点:初始size=0,适合动态填充数据
    
  2. 迭代器范围构造

    std::vector<std::pair<int, std::string>> vec = {{101, "Alice"}, {102, "Bob"}
    };
    std::map<int, std::string> map2(vec.begin(), vec.end());
    // ✅ 输出:{101:"Alice", 102:"Bob"}
    
  3. 初始化列表构造(C++11)

    std::map<std::string, int> map3{{"Apple", 3}, {"Banana", 5}, {"Cherry", 2}
    };
    // ✅ 输出:{"Apple":3, "Banana":5, "Cherry":2}std::multimap<char, float> multimap2{{'A', 1.1f}, {'A', 1.2f}, {'B', 2.0f}
    };
    // ✅ 输出:{'A':1.1, 'A':1.2, 'B':2.0} (multimap允许重复键)
    
  4. 拷贝赋值

    std::map<int, std::string> map4 = map2;  // 深拷贝 
    // ✅ map4内容:{101:"Alice", 102:"Bob"} (独立副本)
    
  5. 移动赋值(C++11)

    std::map<int, std::string> map5 = std::move(map4);
    // ✅ 结果:
    //   map5: {101:"Alice", 102:"Bob"} 
    //   map4变为空容器 (资源所有权转移)
    
  6. 初始化列表赋值(C++11)

    map5 = {{201, "Tom"}, {202, "Jerry"}};  // 覆盖原内容
    // ✅ 新内容:{201:"Tom", 202:"Jerry"}
    
  7. 隐式析构

    {std::map<int, int> tempMap{{1,10}, {2,20}};
    } // 离开作用域时自动调用 ~map() 释放内存 
    // ✅ 无需手动调用,RAII机制自动管理 
    
3. 详细说明
  1. 排序特性

    std::map<int, int> {{3,30}, {1,10}, {2,20}};
    // 自动排序为:{1:10, 2:20, 3:30}
    
  2. 重复键处理

    std::map<std::string, int> {{"A",1}, {"A",2}}; 
    // 仅保留最后一个值 {"A":2}
    
  3. multimap特性

    std::multimap<int, std::string> mm{{1, "A"}, {1, "B"}, {2, "C"}  // ✅ 允许重复键
    };
    

    查找时需用 equal_range() 获取重复键范围

  4. 自定义比较器

    struct CaseInsensitiveCompare 
    {bool operator()(const std::string& a, const std::string& b) const {return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(),[](char c1, char c2) { return std::tolower(c1) < std::tolower(c2); });}
    };
    std::map<std::string, int, CaseInsensitiveCompare> caseInsensitiveMap;
    
  5. 自定义比较器

    auto cmp = [](const std::string& a, const std::string& b) {return a.length() < b.length();  // 按长度排序
    };
    std::map<std::string, int, decltype(cmp)> customMap(cmp);
    
  6. 性能警告
    避免在循环中反复构造/赋值大容器,优先使用移动语义:

    // 推荐做法 ✅ 
    std::map<int, Data> process(MapType&& input) {return std::move(input);  // 零拷贝返回 
    }
    

2. 迭代器操作

1. 函数说明
函数签名功能说明
begin()/cbegin()返回首元素迭代器
end()/cend()返回尾后迭代器
rbegin()/crbegin()返回反向迭代器
rend()/crend()返回反向尾后迭代器
2. 用法示例
  1. 正向遍历

    std::map<std::string, int> m{{"A",1}, {"B",2}, {"C",3}};// 普通迭代器(可修改元素)
    auto it = m.begin();  // 获取首元素迭代器 
    std::cout << it->first << ":" << it->second  <<std::endl;  // 输出 A:1 
    it->second = 10;      // ✅ 允许修改值     //输出  A:10 B:2 C:3
    print_container(m);// 常量迭代器(C++11起)
    auto cit = m.cbegin(); // 常量迭代器 
    //
    
  2. 遍历整个容器

    // 传统循环(C++03风格)
    for (auto it = m.begin(); it != m.end(); ++it) {std::cout << it->first << "→" << it->second << " ";
    }
    // 输出:A→10 B→2 C→3 // 反向遍历(C++11起)
    for (auto rit = m.rbegin(); rit != m.rend(); ++rit) {std::cout << rit->first << " ";  // 输出:C B A 
    }
    
  3. 反向迭代器特殊用法

    // 获取最后一个有效元素 
    auto last_it = m.rbegin();  
    std::cout << last_it->first;  // 输出 C // 反向迭代器转正向迭代器 
    auto normal_it = last_it.base();  // 指向结束位置 
    std::cout << (--normal_it)->first;  // 输出 C 
    
  4. 常量容器场景

    const std::map<int, std::string> cm{{1,"一"}, {2,"二"}};
    // auto mod_it = cm.begin();  ❌ 普通迭代器被禁用 
    auto const_it = cm.cbegin();  // ✅ 必须用常量迭代器 
    std::cout << const_it->second;  // 输出 一 
    
3. 详细说明

迭代器关键特性总结

  1. 迭代器失效规则

    • 插入/删除元素可能使当前迭代器失效
    • 修改 key 会导致未定义行为(需先删除再插入)
  2. 性能特性

    m.begin();   // O(1) 时间复杂度  
    m.rbegin();  // O(1) (底层实现为 end()-- 操作)
    
  3. 通用性扩展

    // 适用于所有STL有序容器 
    std::multimap<int, int> mm;
    auto mm_it = mm.crbegin();  // ✅ 反向常量迭代器 
    
  4. 结构化绑定迭代

    for (const auto& [key, value] : countryCodes) {std::cout << key << " : " << value << "\n";
    }
    

3. 容量查询

1. 函数说明
函数签名功能说明
empty() const检查容器是否为空
size() const返回元素数量
max_size() const返回最大可能元素数
2. 用法示例
  1. empty() const - 空容器检测

    std::map<std::string, int> inventory;// 检查容器是否为空 
    if (inventory.empty()) {std::cout << "仓库为空,请及时补货!\n";// ✅ 输出:仓库为空,请及时补货!
    }inventory.insert({"手机", 50});
    if (!inventory.empty()) {std::cout << "仓库有存货,可正常销售\n";// ✅ 输出:仓库有存货,可正常销售 
    }
    

    关键应用场景:

    • 数据加载前的预检查
    • 循环遍历前的安全防护
    • 资源释放前的状态确认
  2. size() const - 元素数量统计

    std::multimap<int, std::string> employees{
    {101, "刘备"}, {102, "关羽"}, {102, "张飞"} };// 获取当前元素数量 
    size_t count = employees.size();
    std::cout << "公司员工总数:" << count << "\n";
    // ✅ 输出:公司员工总数:3 // 与empty()结合使用 
    while (employees.size() > 0) {auto it = employees.begin();std::cout << "解雇员工:" << it->second << "\n";employees.erase(it);
    }
    /* ✅ 输出:
    解雇员工:刘备
    解雇员工:关羽
    解雇员工:张飞
    */
    

    关键特性:

    容器类型size() 行为时间复杂度
    std::map返回唯一键值对数量O(1)
    std::multimap返回所有元素数量(含重复键)O(1)
  3. max_size() const - 容量极限检测

    std::map<int, double> dataMap;// 获取理论最大容量 
    size_t maxCap = dataMap.max_size();
    std::cout << "最大可存储元素数:" << maxCap << "\n";
    /* ✅ 输出示例(64位系统):
    最大可存储元素数:384307168202282325
    */// 实际应用:内存预判 
    size_t required = 1'000'000;
    if (required < dataMap.max_size()) {} 
    else {std::cerr << "超出系统支持的最大容量!";
    }
    
3. 详细说明

使用建议

  1. 优先选择 empty() 而非 size()==0

    // ✅ 推荐做法 
    if (container.empty()) { ... }// ⚠️ 不推荐(某些容器size()非O(1))
    if (container.size() == 0) { ... }
    
  2. max_size() 的实用价值

    // 典型应用:内存敏感系统 
    if (inputData.size() > container.max_size() / 2) {throw std::bad_alloc("内存需求超警戒线");
    }
    

4. 修改器(基础操作)

1. 函数说明
函数签名功能说明
insert(const value_type&)插入元素(键存在时行为不同,见差异表)
emplace(Args&&...)原位构造元素 (C++11)
emplace_hint(const_iterator, Args&&...)带位置提示的原位构造
erase(iterator pos)删除指定位置元素
erase(const key_type& key)删除所有匹配键的元素
clear()清空所有元素
swap(map& other)交换容器内容
extract(const_iterator)移除节点并返回节点句柄 (C++17)
extract(const key_type&)按键移除节点 (C++17)
merge(map& source)合并两个容器 (C++17)
2. 用法示例
  1. insert(const value_type&)

    std::map<int, std::string> m;
    // ✅ 单元素插入
    auto [it1, success1] = m.insert({101, "Alice"});
    // success1=true, it1->second="Alice"
    std::cout <<it1->first<<"->" <<it1->second <<' '<< std::boolalpha<<success1<<std::endl;  // 🔑 键已存在时不覆盖(multimap允许重复)
    auto [it2, success2] = m.insert({101, "Bob"});
    // success2=false, 值仍为"Alice"
    std::cout <<it2->first<<"->" <<it2->second <<' '<< std::boolalpha<<success2<<std::endl;  
    
    • std::map 容器本身没有 first/second 成员,该语法属于 std::pair 元素
  2. emplace(Args&&...) (C++11)

    // ✅ 免拷贝构造(效率更高)
    auto [it, success] = m.emplace(102, "Charlie"); 
    // 等效于 m.insert(std::make_pair(102, "Charlie"))
    
  3. emplace_hint(const_iterator, Args&&...)

    // ✅ 提示插入位置(优化红黑树平衡)
    auto hint = m.find(102);
    m.emplace_hint(hint, 103, "David");  // 在102后快速插入
    
  4. erase(iterator) / erase(key)

    // 删除特定位置
    auto it = m.find(101);
    if (it != m.end()) m.erase(it);  // ✅ 删除101// 删除所有匹配键(multimap删除全部重复键)
    std::multimap<int, std::string> mm{{1,"A"}, {1,"B"}};
    mm.erase(1);  // ✅ 两个元素均被删除 
    
  5. clear()

    std::map<int, int> data{{1,10}, {2,20}};
    data.clear();  // ✅ 清空容器 
    std::cout << data.size();  // 输出:0
    
  6. swap(map& other)

    std::map<int, std::string> mapA{{1,"X"}};
    std::map<int, std::string> mapB{{2,"Y"}};mapA.swap(mapB);  // ✅ 高效交换(O(1)复杂度)
    // mapA: {2:"Y"}, mapB: {1:"X"}
    
  7. extract() (C++17)

    std::map<int, std::string> src{{3,"Apple"}, {4,"Banana"}};
    std::map<int, std::string> dst;// 提取节点(不销毁元素)
    auto node = src.extract(3);  // ✅ 从src移除 
    node.key() = 5;  // 修改键(提取后允许修改key)
    dst.insert(std::move(node)); // 插入新容器
    // dst: {5:"Apple"}, src: {4:"Banana"}
    
  8. merge(map& source) (C++17)

    std::map<int, std::string> main{{1,"A"}};
    std::map<int, std::string> sub{{2,"B"}, {3,"C"}};main.merge(sub);  // ✅ 合并sub到main
    // main: {1:"A", 2:"B", 3:"C"}, sub变为空
    
3. 详细说明
  • ⚡ 操作特性对比表

    操作时间复杂度键冲突处理容器兼容性
    insertO(log n)map:拒绝插入;multimap:允许map/multimap
    emplaceO(log n)同insertmap/multimap
    emplace_hintO(1)~O(log n)同insertmap/multimap
    erase(iterator)O(1)精确删除单个元素map/multimap
    extractO(log n)允许修改提取后的键map/multimap(C++17)
    mergeO(n log n)保留原容器所有元素同类型容器(C++17)
  • 在频繁修改键的场景优先使用 extract() + insert()(避免拷贝开销);

  • 批量操作使用 merge() 比循环插入快 50% 以上。

5. 查找操作

1. 函数说明
函数签名功能说明
find(const key_type&)返回匹配键的迭代器(未找到返回 end()
count(const key_type&) const返回匹配键的元素数量
equal_range(const key_type&)返回匹配键的范围 pair<iter, iter>
lower_bound(const key_type&)返回首个 ≥ key 的迭代器
upper_bound(const key_type&)返回首个 > key 的迭代器
2. 用法示例
  1. find(const key_type&)

    std::map<int, std::string> m{{101,"A"}, {103,"C"}, {105,"E"}};auto it = m.find(103);  // ✅ 查找键103 
    if (it != m.end()) {std::cout << "找到: " << it->second<< std::endl;  // 输出:C 
    } 
    else {std::cout << "未找到"<< std::endl;
    }
    // 查找不存在的键:
    auto it2 = m.find(102);  // it2 == m.end()
    if (it2 == m.end()) std::cout << "未找到"<< std::endl;
    
  2. count(const key_type&)

    std::map<int, char> cnt{{1,'a'}, {2,'b'}, {2,'c'}}; // map自动去重 std::cout << "键2的数量: " << cnt.count(2)<< std::endl;  // ✅ 输出:1 
    std::cout << "键3的数量: " << cnt.count(3)<< std::endl;  // 输出:0// ⚠️ 对比multimap
    std::multimap<int, char> mm{{1,'x'}, {1,'y'}};
    std::cout << mm.count(1);  // 输出:2(计数重复键)
    
  3. equal_range(const key_type&)

    std::multimap<int, std::string> mm{{10, "A"}, {20, "B"}, {20, "C"}, {30, "D"}
    };auto [first, last] = mm.equal_range(20);  // ✅ C++17结构化绑定 
    for (auto it = first; it != last; ++it) {std::cout << it->second << " ";  // 输出:B C 
    }
    
  4. lower_bound(const key_type&)

    std::map<int, char> data{{10,'J'}, {20,'K'}, {30,'L'}, {40,'M'}
    };auto lb = data.lower_bound(25);  // ✅ 首个 ≥25的键
    if (lb != data.end()) {std::cout << lb->first << ":" << lb->second;  // 输出:30:L 
    }// 边界测试
    auto lb0 = data.lower_bound(5);   // → 指向10:J
    auto lbMax = data.lower_bound(50); // → 返回end()
    
  5. upper_bound(const key_type&)

    std::map<char, int> letters{
    {'a',1}, {'c',3}, {'e',5}, {'g',7}
    };auto ub = letters.upper_bound('d');  // ✅ 首个 >'d'的键 
    std::cout << ub->first<< std::endl;  // 输出:e// 与lower_bound配合实现范围查询
    auto lb = letters.lower_bound('b');  // → 指向'c'
    auto ub2 = letters.upper_bound('f'); // → 指向'g'
    for (; lb != ub2; ++lb) {std::cout << lb->first<<' ';  // 输出:c e
    }
    
3. 详细说明
  • 查找函数特性对比表

    函数返回类型典型应用场景时间复杂度
    find(key)迭代器精确查找单个元素O(log n)
    count(key)size_t检查键是否存在O(log n)
    equal_range(key)pair<iter, iter>获取重复键的范围(multimap)O(log n)
    lower_bound(key)迭代器范围查询起点/键不存在时插入点O(log n)
    upper_bound(key)迭代器范围查询终点O(log n)
  • 应用场景示例:成绩分段统计

    std::map<int, int> scoreRanges{{60, 1}, {70, 1}, {80, 2}, {90, 2}, {100, 1}
    };// 统计[75, 85)区间的成绩数量 
    auto low = scoreRanges.lower_bound(75);  // 指向80 
    auto high = scoreRanges.upper_bound(85); // 指向90(不含)int count = 0;
    for (auto it = low; it != high; ++it) {count += it->second;
    }
    std::cout << "75~85分人数: " << count;

    注意事项

    1. 迭代器有效性

      • find() 未找到时返回 end(),不可解引用
      • lower_bound(100) 在最大键为90时返回 end()
    2. multimap 专用操作

      // 遍历重复键的两种方式 
      // 方式1:equal_range
      auto [start, end] = mm.equal_range(key);// 方式2:lower_bound + upper_bound
      auto lb = mm.lower_bound(key);
      auto ub = mm.upper_bound(key);
      
    3. 性能优化

      • 多次查找时优先缓存 end() 迭代器
      auto endIt = m.end();
      for (auto key : keysToFind) {if (m.find(key) != endIt) { ... }
      }
      
  • 在需要同时检查存在性和获取值的场景,优先使用 find() 而非 count() + operator[],避免两次查找开销。

  • 范围查询时,lower_bound/upper_bound 组合比循环遍历快 10 倍以上。

6. 观察器

1. 函数说明
函数签名功能说明
key_comp() const返回键比较函数(如 less<Key>
value_comp() const返回值比较函数
get_allocator() const返回关联的分配器
2. 用法示例
  1. key_comp():获取键比较器

    #include <map>int main() {std::map<int, std::string> m;auto key_compare = m.key_comp();   // 获取键比较器(默认 less<int>)// 验证比较器逻辑 bool cmp1 = key_compare(10, 20);  // true(10 < 20)bool cmp2 = key_compare(30, 15);  // false(30 ≮ 15)std::cout <<cmp1<< ' ' <<cmp2<< std::endl;//1 0return 0;
    }
    
  2. value_comp():获取值比较器

    #include <map>int main() {std::map<int, char> m{{1,'A'}, {3,'C'}, {5,'E'}};auto value_compare = m.value_comp();  // 比较器基于键(first元素)// 比较两个pair对象 auto p1 = std::pair<const int, char>(2, 'B');auto p2 = std::pair<const int, char>(4, 'D');bool cmp = value_compare(p1, p2);  // true(2 < 4)std::cout <<cmp<< std::endl;//1return 0;
    }
    
  3. get_allocator():获取内存分配器

    #include <map>
    #include <memory>
    #include <iostream>  // 新增输出 int main() {std::map<int, double> m;auto alloc = m.get_allocator(); // ========== 验证1:分配器基本信息 ==========std::cout << "1. 分配器验证\n";std::cout << "分配器类型: " << typeid(decltype(alloc)).name() << "\n";std::cout << "支持的最大内存: " << alloc.max_size()  << " bytes\n\n";// ========== 内存分配阶段 ==========using value_type = std::pair<const int, double>;std::cout << "2. 分配内存前指针状态: " << std::hex << (void*)nullptr << std::dec << std::endl;auto ptr = std::allocator_traits<decltype(alloc)>::allocate(alloc, 1);std::cout << "3. 分配内存后地址: " << std::hex << ptr << std::dec << " | 分配单元数: 1\n\n";// ========== 对象构造阶段 ==========std::cout << "4. 构造对象前内存内容: " << "key=" << ptr->first << ", value=" << ptr->second << " (随机值)\n";std::allocator_traits<decltype(alloc)>::construct(alloc, ptr, 101, 3.14);std::cout << "5. 构造对象后验证: key=" << ptr->first << ", value=" << ptr->second << "\n\n";// // ========== 销毁释放阶段 ==========std::cout << "6. 销毁对象前访问测试: " << ptr->second << " (正常访问)\n";std::allocator_traits<decltype(alloc)>::destroy(alloc, ptr);std::cout << "7. 对象已销毁 | 内存地址保留: " << std::hex << ptr << std::dec << "\n";// 安全防护:阻止悬空指针访问(演示用异常处理)try {std::cout << "8. 尝试访问已销毁对象: ";std::cout << ptr->second << std::endl;  // 实际会崩溃 } catch(...) {std::cout << "捕获到异常(预期行为)\n";}std::allocator_traits<decltype(alloc)>::deallocate(alloc, ptr, 1);std::cout << "9. 内存已释放 | 原地址状态: " << std::hex << ptr << " → 悬空指针\n\n";return 0;
    }
    

    输出结果:

    分配器类型: SaISt4pairIKidEE
    支持的最大内存: 1152921504606846975 bytes2. 分配内存前指针状态: 0
    3. 分配内存后地址: 0xe31730 | 分配单元数: 14. 构造对象前内存内容: key=14883488, value=7.35022e-317 (随机值)
    5. 构造对象后验证: key=101, value=3.146. 销毁对象前访问测试: 3.14 (正常访问)
    7. 对象已销毁 | 内存地址保留: 0xe31730
    8. 尝试访问已销毁对象: 3.14
    9. 内存已释放 | 原地址状态: 0xe31730 → 悬空指针
    
3. 详细说明
  1. key_comp() 本质

    • 返回的实际上是 Compare 类型的函数对象
    • 可通过该比较器判断两个键的排序关系
    if (m.key_comp()(k1, k2)) { // k1 在 k2 之前 
    }
    
  2. value_comp() 特性

    • std::map 中相当于:
    [](const auto& a, const auto& b) { return key_comp()(a.first, b.first); 
    }
    
  3. 分配器高级应用

    操作标准方法推荐方式
    内存分配alloc.allocate(n)allocator_traits::allocate
    对象构造alloc.construct(ptr, args...)allocator_traits::construct
    内存释放alloc.deallocate(ptr, n)allocator_traits::deallocate

在需要跨容器共享内存池或实现特殊内存管理的场景中,优先通过 get_allocator() 传递分配器,而非直接使用 new/delete

7. 非成员函数

1. 函数说明
函数签名功能说明
operator==(const map&, const map&)内容相等性比较
operator!=(const map&, const map&)不等性比较
std::swap(map& a, map& b)特化交换函数
2. 用法示例
  1. operator==operator!= 示例

    #include <map>
    #include <string>int main() {std::map<int, std::string> m1{{1, "apple"}, {2, "banana"}};std::map<int, std::string> m2{{1, "apple"}, {2, "banana"}};std::map<int, std::string> m3{{3, "cherry"}};// 比较内容是否完全一致(键值对和顺序)bool eq1 = (m1 == m2); // true(内容完全一致)bool eq2 = (m1 == m3); // false(内容不同)// 不等性比较(等价于 !(m1 == m3))bool neq = (m1 != m3); // true 
    }
    
  2. std::swap 特化示例

    #include <map>
    #include <iostream>int main() {std::map<int, char> a{{1, 'A'}, {2, 'B'}};std::map<int, char> b{{3, 'C'}};// 交换两个 map 的内容(高效交换底层数据结构)std::swap(a, b);// 验证交换结果 std::cout << "a size: " << a.size() << ", b size: " << b.size();// 输出: a size: 1, b size: 2 
    }
    
3. 详细说明
  1. operator==
    严格比较键值对内容及顺序(map 内部按照排序,顺序由的比较逻辑决定)。

  2. std::swap 特化
    时间复杂度为 O(1),直接交换底层数据指针,比 a.swap(b)(成员函数版本)更通用,支持非左值操作。

  3. C++11 及更新标准
    operator!= 已由编译器自动生成,无需手动实现,直接使用即可。

三、map 和 multimap 功能差异对比表

1. 函数说明
函数/行为std::mapstd::multimap
键唯一性键必须唯一,重复插入失败允许重复键
insert(const value_type&)键存在时返回 pair<iter, false>总是插入成功,返回新元素迭代器
count(const key_type&)返回值始终为 0 或 1返回键的实际重复数量
erase(const key_type&)删除 0 或 1 个元素删除所有匹配键的元素
equal_range() 返回值范围范围长度 ≤ 1范围长度可能 > 1
2. 用法示例
  1. insert(const value_type&)

    std::map<int, std::string> m;
    // 插入新键(成功)
    auto [it1, success1] = m.insert({1,  "Apple"});  // it1指向新元素,success1=true 
    std::cout <<it1->first<<" : " <<it1->second <<' '<<
    std::boolalpha<<success1<<std::endl;  //1 : Apple true  // 插入重复键(失败)
    auto [it2, success2] = m.insert({1,  "Banana"}); // it2指向已存在元素,success2=false
    std::cout <<it2->first<<" : " <<it2->second <<' '<<
    std::boolalpha<<success2<<std::endl;  //1 : Apple false
    

    特点:

    • 键不存在时插入新元素,返回 <新元素迭代器, true>
    • 键存在时返回 <已有元素迭代器, false>,不覆盖原值
  2. count(const key_type&)

    std::map<char, int> m{{'A', 1}, {'B', 2}};
    size_t c1 = m.count('A');  // 返回1(键存在)
    size_t c2 = m.count('X');  // 返回0(键不存在)
    

    特点:
    - 返回值只能是 01(因 map 键唯一)
    - 比 find() 更简洁的键存在性检查

  3. erase(const key_type&)

    std::map<std::string, int> m{{"Cat", 3}, {"Dog", 4}};
    size_t n1 = m.erase("Cat");  // 删除成功,n1=1
    size_t n2 = m.erase("Bird"); // 键不存在,n2=0
    

    特点:
    - 删除成功返回 1,失败返回 0
    - 仅删除一个元素(因键唯一)

  4. equal_range()

    std::map<int, char> m{{10,'a'}, {20,'b'}, {30,'c'}};
    auto [first, last] = m.equal_range(20);// 遍历结果范围(最多包含一个元素)
    for (auto it = first; it != last; ++it) {std::cout << it->first << ": " << it->second;  // 输出"20: b"
    }
    

    特点:
    - 返回匹配键的迭代器范围 [first, last)
    - 范围长度 ≤1(因键唯一),若键不存在则 first == last

3. 详细说明

关键总结

函数行为特点返回值说明
insert()不覆盖已有键值<迭代器, 插入是否成功>
count()快速键存在性检查0(不存在)或 1(存在)
erase()精确删除单个元素实际删除的元素数量(0或1)
equal_range()返回单元素范围兼容关联容器API的统一访问方式

📌 注意:所有函数均基于 std::map 的键唯一特性设计,与 std::multimap 的行为有本质区别。

四、map 独有函数

1. 函数说明
函数签名功能说明
operator[](const key_type&)键存在返回值引用;不存在时插入新元素
operator[](key_type&&)右值键版本 (C++11)
at(const key_type&)键存在返回常量引用;不存在抛异常
at(const key_type&) constconst 重载版本
try_emplace(const key_type&, Args&&...)键不存在时原位构造 (C++17)
insert_or_assign(const key_type&, M&&)键存在时赋值,不存在时插入 (C++17)
2. 用法示例
  1. operator[](const key_type&)

    std::map<std::string, int> scores;
    scores["Alice"] = 92;   // 插入新键值对
    scores["Bob"] = 85;     // 插入新键值对
    int aliceScore = scores["Alice"];  // 获取值(键存在)
    int eveScore = scores["Eve"];     // 自动插入{Eve, 0}并返回值 std::cout <<aliceScore<< std::endl;//92
    std::cout <<eveScore<< std::endl;  //0
    

    特点:

    • 键存在 → 返回值的可变引用
    • 键不存在 → 自动插入 key_type() 初始化的值(如 int()0
    • ⚠️ 危险:可能意外插入(不需要的)元素(用 if (map.find(key) != ...) 防御)
  2. operator[](key_type&&) (C++11)

    std::map<std::string, int> m;
    std::string key = "temp";
    auto& val = m[std::move(key)] = 10;  // 移动语义避免拷贝std::cout << val        << "\n";    //10
    std::cout << m["temp"]  << "\n";    //10
    std::cout << key        << "\n";    // 被移走了为空
    // key 状态未定义(可能为空)
    

    特点:

    • 右值键版本,避免临时对象拷贝(提高性能)
    • 行为与左值版本一致,但转移键的所有权
  3. at(const key_type&)

    std::map<int, std::string> idMap{{101, "Alice"}, {102, "Bob"}};
    std::string name1 = idMap.at(101);  // 成功返回"Alice"
    try {std::string name2 = idMap.at(103);  // 抛 std::out_of_range 
    } catch (const std::exception& e) {std::cerr << "Error: " << e.what();
    }
    

    特点:

    • 键存在 → 返回常量引用
    • 键不存在 → 抛出 std::out_of_range 异常
    • 安全替代方案:优先于 operator[] 用于只读访问
  4. at(const key_type&) const

    const std::map<int, char> vowels{{97, 'a'}, {101, 'e'}};
    char a = vowels.at(97);  // 返回 'a'(const引用)
    // vowels.at(105);  // 抛异常(i不存在)
    

    特点:

    • const 容器专属版本
    • 返回只读引用(禁止修改值)
    • 同样在键不存在时抛出异常
  5. try_emplace() (C++17)

    std::map<int, std::string> users;
    // 原位构造避免临时对象
    auto [it1, inserted1] = users.try_emplace(1, "Alice");  // 插入成功 
    auto [it2, inserted2] = users.try_emplace(1, "Ali");    // 失败,值不变// 构造参数可拆分 
    auto [it3, inserted3] = users.try_emplace(2, 5, 'X');  // 值为"XXXXX"
    

    特点✨:

    • 键不存在 → 原位构造值(避免拷贝/移动)
    • 键存在 → 忽略操作(不修改值)
    • 高效替代:优先于 insert 用于复杂对象
  6. insert_or_assign() (C++17)

    std::map<std::string, int> config;
    // 插入新配置
    auto [it1, action1] = config.insert_or_assign("Timeout", 30); // 插入// 更新现有配置
    auto [it2, action2] = config.insert_or_assign("Timeout", 60); // 赋值// 输出:action1=true(插入),action2=false(赋值)
    

    特点✨:

    • 键不存在 → 插入新元素
    • 键存在 → 赋值新值(覆盖旧值)
    • 返回值:pair<迭代器, bool>bool=true 表示插入,false 表示赋值)
3. 详细说明

✅ 核心区别总结

场景operator[]at()try_emplace()insert_or_assign()
键不存在时插入默认值抛异常原位构造新值插入新值
键存在时返回值引用返回值忽略操作赋值覆盖旧值
是否可能修改容器仅插入时
异常安全中等

✅最佳推荐📌

  • 需要安全性 → 用 at() 或先 find()
  • 避免意外插入 → 禁用 operator[],改用 try_emplace/insert_or_assign
  • 高性能场景 → 用右值版本或原位构造函数

五、map 和 multimap 关键差异说明

  1. 插入行为
    • map 插入重复键时直接失败(返回 false
    • multimap 无条件插入新元素,即使键重复
  2. 元素访问
    • map 支持 operator[]at() 的直接键访问
    • multimap 必须通过迭代器或 equal_range() 访问元素
  3. 删除操作
    • map::erase(key) 最多删除 1 个元素
    • multimap::erase(key) 删除所有匹配键的元素(返回删除数量)
  4. 核心语义差异
    • map 是键值映射容器
    • multimap 是键值对集合容器

六、完整用法示例

  1. 核心初始化方法

    #include <map>
    #include <string>
    #include <vector>int main() {// 1. 默认初始化 (空map)std::map<std::string, int> emptyMap;  // {}// 2. 初始化列表 (最常用)std::map<std::string, int> countryCodes{{"China", 86}, {"Japan", 81},{"USA", 1}      // {"China":86, "Japan":81, "USA":1}};// 3. 范围初始化 (从其他容器)std::vector<std::pair<std::string, int>> vec = {{"A", 1}, {"B", 2}};std::map<std::string, int> fromRange(vec.begin(),  vec.end());   // {"A":1, "B":2}// 4. 复制初始化std::map<std::string, int> copyMap(countryCodes);  // 深拷贝// 5. 移动初始化 (高效转移)std::map<std::string, int> movedMap(std::move(countryCodes));  // 原map变为空 // 6. 自定义比较器初始化 auto cmp = [](const std::string& a, const std::string& b) {return a.length()  < b.length();   // 按字符串长度排序 };std::map<std::string, int, decltype(cmp)> customMap(cmp);customMap.insert({{"apple",  5}, {"kiwi", 4}});  // 存储顺序: kiwi:4 apple:5// 7. 插入式初始化std::map<int, std::string> idName;idName.insert({101,  "Alice"});   // {101:"Alice"}idName.emplace(102,  "Bob");      // {101:"Alice", 102:"Bob"}// 8. 下标初始化 std::map<std::string, double> exchangeRate;exchangeRate["USD"] = 1.0;       // {"USD":1.0}exchangeRate["EUR"] = 0.93;      // {"USD":1.0, "EUR":0.93}return 0;
    }
    
  2. 用法示例

    映射 模拟了一个简单的投票系统

    #include <iostream>
    #include <map>
    #include <string>
    using namespace std;// 候选人类:存储候选人信息及得票数 
    class Candidate {
    public:// 构造函数:初始化候选人姓名和得票数(默认为0)Candidate (const string& name = "") :m_name (name), m_votes (0) {}// 获取候选人姓名(const成员函数,保证不修改对象状态)	const string& getname (void) const {return m_name;}// 获取候选人得票数size_t getvotes (void) const {return m_votes;}// 投票操作:增加候选人得票数 void vote (void) {m_votes++;}private:string m_name;size_t m_votes;};// 自定义比较器类:实现字符的降序排序
    class Comparator {
    public:// 重载函数调用运算符:比较两个字符bool operator() (char a, char b) const {return a > b;}
    };int main (void) {// 创建map容器:键类型char,值类型Candidate,使用自定义降序比较器map<char, Candidate, Comparator> mc;mc['D'] = Candidate ("刘备");mc['C'] = Candidate ("关羽");mc.insert (pair<char, Candidate> ('A',Candidate ("张飞")) );mc.insert (make_pair ('B', Candidate ("赵云")) );mc['E'] = Candidate ("曹操");// 使用类型别名简化迭代器声明 using IT = map<char,Candidate>::iterator ;		// 非常量迭代器 using CIT= map<char,Candidate>::const_iterator ;// 常量迭代器// 进行10轮投票for (size_t i = 0; i < 10; i++) {// 显示投票选项(按键的降序排列:E→D→C→B→A)for (CIT it = mc.begin (); it != mc.end ();it++)// 输出选项格式:(键) 候选人姓名 cout << '(' << it -> first << ") " <<it -> second.getname () << ' ';cout << endl << "请投票(大写):" << flush;// flush确保立即显示 char cKey;cin >> cKey;// 接收用户投票输入// 在map中查找输入的键IT it = mc.find (cKey);if (it == mc.end ()) {cout << "废票!" << endl;continue;// 跳过后续操作,进入下一轮投票}it -> second.vote (); // 为找到的候选人投票}// 统计投票结果:找出得票最高的候选人CIT win = mc.begin ();   // 初始化获胜者为第一个候选 for (CIT it = mc.begin (); it != mc.end ();it++) {// 输出每位候选人的得票情况cout << it -> second.getname () << "得"<< it -> second.getvotes () << "票。" << endl;// 更新获胜者(得票更高者)if (it -> second.getvotes () > win -> second.getvotes ())win = it;}// 宣布当选结果cout << win -> second.getname () << "当选!"<< endl;// 演示operator[]访问:输出键'C'对应的候选人cout << mc['C'].getname () << endl;return 0;
    }
    

    依次输入:EDCBAAAAA

    输出结果:

    曹操得1票。
    刘备得1票。
    关羽得1票。
    赵云得1票。
    张飞得5票。
    张飞当选!
    关羽

    自定义排序

    • Comparator 重载 operator() 实现字符降序排序(E→D→C→B→A)
    • 面向对象设计:Candidate封装票数操作,隔离数据细节
  3. 用法示例

    多重映射 模拟了一个简单的工资结算清单

    #include <iostream>
    #include <map>int main (void) {// 创建允许重复键的multimap容器,存储<姓名, 数值>对std::multimap<std::string, int> mm;// 插入数据 - multimap允许重复键值 mm.insert (std::make_pair ("张飞", 1000));mm.insert (std::make_pair ("赵云", 1000));mm.insert (std::make_pair ("张飞", 2000));// 重复键"张飞"mm.insert (std::make_pair ("关羽", 2000));mm.insert (std::make_pair ("赵云", 3000));// 重复键mm.insert (std::make_pair ("关羽", 3000));// 重复键// 使用类型别名简化迭代器声明using CIT = std::multimap<std::string, int>::const_iterator;// 第一部分:按排序顺序输出所有数据(按键的字典序排序)for (CIT it = mm.begin (); it != mm.end ();it++)std::cout << it -> first << ":" <<it -> second << std::endl;std::cout << "------------" << std::endl;// 第二部分:计算并输出每个键(姓名)对应的数值总和for (CIT it = mm.begin (); it != mm.end ();it++) {// 关键步骤:找到当前键值范围的结束位置// upper_bound返回第一个大于key的元素位置std::string key = it -> first;CIT end = mm.upper_bound (key);int sum = 0;// 遍历当前键的所有值(从当前位置到end)for (; it != end; it++)sum += it -> second;// 输出当前键的数值总和std::cout << key << ":" << sum << std::endl;// 重要修正:将迭代器回退一位 // 因为内层循环结束时it指向end位置(下一个键的起始)it--;}return 0;
    }结果:
    关羽:2000
    关羽:3000
    张飞:1000
    张飞:2000
    赵云:1000
    赵云:3000
    ------------
    关羽:5000
    张飞:3000
    赵云:4000
    
http://www.lryc.cn/news/627153.html

相关文章:

  • VR交通安全学习机-VR交通普法体验馆方案
  • 2026 济南淀粉深加工展览会亮点:玉米科技与未来产业发展
  • IPSEC安全基础
  • C++智能指针详解:告别内存泄漏,拥抱安全高效
  • 【科研绘图系列】R语言绘制平滑曲线折线图
  • AI重塑软件测试:质量保障的下一站
  • MySQL分库分表与MyCAT
  • PiscCode集成Hand Landmarker:实现高精度手部姿态检测与分析
  • C语言:第18天笔记
  • Google Chrome扩展不受信任 - 不受支持的清单版本 解决方案 -- Chrome扩展插件无法加载 扩展程序无法使用 解决方案
  • PiscCode使用MediaPipe Face Landmarker实现实时人脸特征点检测
  • vue3 el-select 默认选中第一个
  • 基础笔记8.20
  • 8.20网络编程——sqlite3数据库
  • lua入门以及在Redis中的应用
  • 技术干货|使用Prometheus+Grafana监控redis实例详解
  • 自学嵌入式第二十三天:数据结构(3)-双链表
  • Java 性能优化实战(二):JVM 调优的 5 个核心维度
  • c#入门笔记(3)
  • Netty HashedWheelTimer设计原理:从时间轮算法到源码实现
  • 基于SpringBoot的蜗牛兼职网平台
  • RabbitMQ 基础
  • 使用安卓平板,通过USB数据线(而不是Wi-Fi)来控制电脑(版本1)
  • 豆秒数科集团:汽车消费金融市场的领跑者
  • 《P1967 [NOIP 2013 提高组] 货车运输》
  • 层在init中只为创建线性层,forward的对线性层中间加非线性运算。且分层定义是为了把原本一长个代码的初始化和运算放到一个组合中。
  • 常见开源协议详解:哪些行为被允许?哪些被限制?
  • [GraphRAG]完全自动化处理任何文档为向量知识图谱:AbutionGraph如何让知识自动“活”起来?
  • 我从零开始学习C语言(12)- 循环语句 PART1
  • word——如何给封面、目录、摘要、正文设置不同的页码