1. std::unordered_map - 哈希表

为什么需要unordered_map?

// std::map (红黑树,有序) vs std::unordered_map (哈希表,无序)
#include <map>
#include <unordered_map>
#include <iostream>

void compare_containers() {
    std::map<int, std::string> ordered_map = {
        {3, "three"}, {1, "one"}, {2, "two"}
    };
    
    std::unordered_map<int, std::string> unordered_map = {
        {3, "three"}, {1, "one"}, {2, "two"}
    };
    
    std::cout << "std::map (ordered): ";
    for (const auto& p : ordered_map) {
        std::cout << p.first << " "; // 输出: 1 2 3
    }
    
    std::cout << "\nstd::unordered_map (unordered): ";
    for (const auto& p : unordered_map) {
        std::cout << p.first << " "; // 输出: 3 1 2 (顺序不确定)
    }
}

基本用法

#include <unordered_map>
#include <string>

void unordered_map_basics() {
    // 创建和初始化
    std::unordered_map<std::string, int> age_map = {
        {"Alice", 25},
        {"Bob", 30},
        {"Charlie", 35}
    };
    
    // 插入元素
    age_map["David"] = 28;          // 使用下标操作符
    age_map.insert({"Eve", 32});    // 使用insert
    age_map.emplace("Frank", 40);   // 使用emplace (C++11)
    
    // 访问元素
    std::cout << "Alice's age: " << age_map["Alice"] << std::endl;
    std::cout << "Bob's age: " << age_map.at("Bob") << std::endl;
    
    // 检查元素是否存在 (C++20)
    if (age_map.contains("Charlie")) {
        std::cout << "Charlie exists" << std::endl;
    }
    
    // 遍历
    for (const auto& [name, age] : age_map) { // C++17结构化绑定
        std::cout << name << ": " << age << std::endl;
    }
    
    // 删除元素
    age_map.erase("David");
    
    // 大小和容量
    std::cout << "Size: " << age_map.size() << std::endl;
    std::cout << "Bucket count: " << age_map.bucket_count() << std::endl;
}

哈希表性能特性

#include <unordered_map>
#include <iostream>

void hash_performance() {
    std::unordered_map<int, std::string> map;
    
    // 设置负载因子和重新哈希
    map.max_load_factor(0.7f); // 设置最大负载因子
    map.rehash(100);           // 预分配桶空间
    
    // 插入大量数据并观察性能
    for (int i = 0; i < 1000; ++i) {
        map[i] = "value_" + std::to_string(i);
    }
    
    std::cout << "Load factor: " << map.load_factor() << std::endl;
    std::cout << "Bucket count: " << map.bucket_count() << std::endl;
    
    // 查找性能测试
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 10000; ++i) {
        auto it = map.find(i % 1000);
    }
    auto end = std::chrono::high_resolution_clock::now();
    
    std::cout << "Find time: " 
              << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
              << "μs" << std::endl;
}

自定义哈希函数

#include <unordered_map>
#include <string>

struct Person {
    std::string name;
    int age;
    
    // 相等比较运算符
    bool operator==(const Person& other) const {
        return name == other.name && age == other.age;
    }
};

// 自定义哈希函数
struct PersonHash {
    std::size_t operator()(const Person& p) const {
        return std::hash<std::string>()(p.name) ^ (std::hash<int>()(p.age) << 1);
    }
};

void custom_hash_example() {
    std::unordered_map<Person, std::string, PersonHash> person_map;
    
    person_map[{"Alice", 25}] = "Engineer";
    person_map[{"Bob", 30}] = "Designer";
    
    for (const auto& [person, job] : person_map) {
        std::cout << person.name << " (" << person.age << "): " << job << std::endl;
    }
}

2. std::array - 固定大小数组

为什么需要std::array?

// 传统C数组 vs std::array
void compare_arrays() {
    // 传统C数组
    int c_array[5] = {1, 2, 3, 4, 5};
    // 问题:不知道大小(需要额外变量记录)
    // 问题:会退化为指针
    
    // std::array
    std::array<int, 5> std_array = {1, 2, 3, 4, 5};
    // 优点:知道大小,支持STL算法,不会退化为指针
    std::cout << "Size: " << std_array.size() << std::endl;
}

基本用法

#include <array>
#include <algorithm>
#include <iostream>

void array_basics() {
    // 创建和初始化
    std::array<int, 5> arr = {1, 2, 3, 4, 5};
    std::array<std::string, 3> str_arr = {"hello", "world", "!"};
    
    // 访问元素
    std::cout << "First: " << arr.front() << std::endl;   // 1
    std::cout << "Last: " << arr.back() << std::endl;     // 5
    std::cout << "Index 2: " << arr[2] << std::endl;      // 3
    std::cout << "Index 3: " << arr.at(3) << std::endl;   // 4 (带边界检查)
    
    // 迭代器支持
    for (auto it = arr.begin(); it != arr.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    // 范围for循环
    for (int value : arr) {
        std::cout << value << " ";
    }
    std::cout << std::endl;
    
    // STL算法支持
    std::sort(arr.begin(), arr.end());
    auto count = std::count_if(arr.begin(), arr.end(), 
                               [](int x) { return x % 2 == 0; });
    
    // 多维数组
    std::array<std::array<int, 3>, 2> matrix = {
        {{1, 2, 3},
         {4, 5, 6}}
    };
}

编译期特性

#include <array>
#include <type_traits>

constexpr std::array<int, 5> create_array() {
    return {1, 2, 3, 4, 5};
}

void compile_time_features() {
    // 编译期大小信息
    constexpr size_t size = std::tuple_size<std::array<int, 5>>::value;
    static_assert(size == 5, "Size must be 5");
    
    // 编译期创建和操作
    constexpr auto arr = create_array();
    static_assert(arr[0] == 1, "First element must be 1");
    
    // 用于模板元编程
    using MyArray = std::array<int, 10>;
    using ValueType = typename MyArray::value_type;
    static_assert(std::is_same_v<ValueType, int>, "Value type must be int");
}

3. std::tuple - 异构元组

为什么需要std::tuple?

// 传统方式:需要定义结构体
struct Person {
    std::string name;
    int age;
    double salary;
};

// 使用tuple:不需要预先定义类型
void tuple_vs_struct() {
    // 定义结构体
    Person p{"Alice", 25, 50000.0};
    
    // 使用tuple
    std::tuple<std::string, int, double> person{"Alice", 25, 50000.0};
    
    // 临时、一次性的数据结构用tuple更方便
}

基本用法

#include <tuple>
#include <string>
#include <iostream>

void tuple_basics() {
    // 创建tuple
    std::tuple<int, std::string, double> t1(1, "hello", 3.14);
    auto t2 = std::make_tuple(2, "world", 2.71); // 自动推导类型
    std::tuple t3(3, "!", 1.41); // C++17 CTAD
    
    // 访问元素 (C++11方式)
    std::cout << "get<0>: " << std::get<0>(t1) << std::endl;
    std::cout << "get<1>: " << std::get<1>(t1) << std::endl;
    std::cout << "get<2>: " << std::get<2>(t1) << std::endl;
    
    // 修改元素
    std::get<0>(t1) = 100;
    
    // 获取tuple信息
    std::cout << "Tuple size: " << std::tuple_size<decltype(t1)>::value << std::endl;
    
    // 类型获取
    using FirstType = std::tuple_element<0, decltype(t1)>::type;
    static_assert(std::is_same_v<FirstType, int>, "First type must be int");
}

结构化绑定 (C++17)

#include <tuple>
#include <string>

void structured_binding() {
    auto person = std::make_tuple("Alice", 25, 50000.0);
    
    // 传统方式
    std::string name = std::get<0>(person);
    int age = std::get<1>(person);
    double salary = std::get<2>(person);
    
    // C++17结构化绑定
    auto [name2, age2, salary2] = person;
    
    // 引用绑定(可以修改原tuple)
    auto& [name_ref, age_ref, salary_ref] = person;
    name_ref = "Bob"; // 修改原tuple
    
    // 用于函数返回多个值
    auto get_person_info = []() -> std::tuple<std::string, int, double> {
        return {"Charlie", 30, 60000.0};
    };
    
    auto [name3, age3, salary3] = get_person_info();
}

tuple的实用操作

#include <tuple>
#include <iostream>

void tuple_operations() {
    auto t1 = std::make_tuple(1, "hello", 3.14);
    auto t2 = std::make_tuple(2, "world", 2.71);
    
    // 比较操作
    if (t1 < t2) { // 按字典序比较
        std::cout << "t1 < t2" << std::endl;
    }
    
    // 交换
    std::swap(t1, t2);
    
    // 连接tuple (C++11)
    auto t3 = std::tuple_cat(t1, t2, std::make_tuple("extra"));
    
    // 解包调用函数
    void print_info(int id, const std::string& name, double value);
    std::apply(print_info, t1);
    
    // 遍历tuple (需要模板元编程)
}

实际应用场景

#include <tuple>
#include <vector>
#include <algorithm>

// 1. 函数返回多个值
std::tuple<bool, std::string, int> process_data() {
    // 处理逻辑...
    return {true, "success", 42};
}

// 2. 多值排序
void multi_criteria_sort() {
    std::vector<std::tuple<std::string, int, double>> people = {
        {"Alice", 25, 50000.0},
        {"Bob", 30, 45000.0},
        {"Charlie", 25, 55000.0}
    };
    
    // 按年龄升序,薪水降序
    std::sort(people.begin(), people.end(), 
        [](const auto& a, const auto& b) {
            if (std::get<1>(a) != std::get<1>(b)) {
                return std::get<1>(a) < std::get<1>(b);
            }
            return std::get<2>(a) > std::get<2>(b);
        });
}

// 3. 变参模板的存储
template<typename... Args>
class Event {
private:
    std::tuple<Args...> data;
public:
    Event(Args... args) : data(std::make_tuple(args...)) {}
    
    template<size_t Index>
    auto get() const -> const std::tuple_element_t<Index, std::tuple<Args...>>& {
        return std::get<Index>(data);
    }
};

4. 常见问题

Q1: unordered_map 和 map 的区别?

  • std::map:红黑树实现,元素有序,查找时间O(log n)

  • std::unordered_map:哈希表实现,元素无序,平均查找时间O(1),最坏O(n)

  • 选择:需要有序用map,需要快速查找用unordered_map

Q2: std::array 相比C数组有什么优点?

  • 知道自己的大小(size()方法)

  • 支持STL算法和迭代器

  • 不会退化为指针

  • 更好的类型安全

  • 支持复制和赋值

Q3: 什么时候使用std::tuple?

  • 函数需要返回多个值时

  • 临时性的异构数据集合

  • 模板元编程中

  • 避免定义只使用一次的结构体

Q4: 如何选择不同的容器?

  • 需要键值对:map(有序)或 unordered_map(快速)

  • 固定大小数组:std::array

  • 动态数组:std::vector

  • 异构数据:std::tuple 或 std::variant

Q5: C++17对这些容器有什么增强?

  • unordered_mapcontains()方法

  • std::arrayto_array()函数(C++20)

  • std::tuple:结构化绑定,CTAD(类模板参数推导)

5. 最佳实践

使用auto简化代码

void modern_usage() {
    // 使用auto避免冗长的类型声明
    auto scores = std::unordered_map<std::string, int>{
        {"Alice", 95}, {"Bob", 88}, {"Charlie", 92}
    };
    
    auto points = std::array{1, 2, 3, 4, 5}; // C++17 CTAD
    
    auto person = std::make_tuple("David", 28, 75000.0);
    
    // 结构化绑定
    for (const auto& [name, score] : scores) {
        std::cout << name << ": " << score << std::endl;
    }
}

性能考虑

void performance_considerations() {
    // unordered_map: 预分配空间
    std::unordered_map<int, std::string> large_map;
    large_map.reserve(10000); // 预分配,避免重新哈希
    
    // array: 栈上分配,速度快但大小固定
    std::array<int, 1000> fast_array; // 栈上分配
    
    // tuple: 编译期确定,无运行时开销
    constexpr auto config = std::make_tuple(1024, 768, "Window Title");
}

总结

C++11引入的这些容器大大增强了标准库的能力:

  • ✅ std::unordered_map:高性能哈希表,快速查找

  • ✅ std::array:类型安全的固定大小数组

  • ✅ std::tuple:灵活的异构数据容器

  • ✅ 现代C++特性:支持auto、结构化绑定、CTAD等

  • ✅ 性能优势:编译期优化,运行时高效

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐