• C++
  • C++ STL 容器 vector 重载运算符教程

  • @ 2025-5-29 11:57:23

C++ STL 容器 vector 重载运算符教程

1. vector 简介

定义:vector 是 C++ 标准库中的动态数组容器,支持随机访问和动态扩容。

头文件

#include <vector>

声明

std::vector<数据类型> 变量名;

示例

std::vector<int> numbers;          // 空 vector
std::vector<double> scores(5, 0.0); // 包含5个0.0的vector

2. vector 重载的运算符

vector 容器重载了多个运算符,使其使用起来更像普通数组:

运算符 功能描述
[] 访问指定位置的元素
= 赋值运算符
== 判断两个 vector 是否相等
!= 判断两个 vector 是否不等
< 比较两个 vector 的大小
<=
>
>=
<< 输出 vector 的元素(需自定义)

3. 下标运算符 []

功能:访问 vector 中指定位置的元素。

示例

#include <vector>
#include <iostream>

int main() {
    std::vector<int> numbers = {10, 20, 30, 40, 50};
    
    // 使用[]访问元素
    std::cout << "第一个元素: " << numbers[0] << std::endl;  // 输出: 10
    
    // 修改元素
    numbers[2] = 300;
    std::cout << "修改后的第三个元素: " << numbers[2] << std::endl;  // 输出: 300
    
    return 0;
}

注意[] 不进行边界检查,使用超出范围的索引会导致未定义行为。

4. 赋值运算符 =

功能:将一个 vector 的内容复制给另一个 vector。

示例

#include <vector>
#include <iostream>

int main() {
    std::vector<int> source = {1, 2, 3};
    std::vector<int> destination;
    
    // 使用赋值运算符
    destination = source;
    
    // 输出 destination 的内容
    for (int num : destination) {
        std::cout << num << " ";  // 输出: 1 2 3
    }
    std::cout << std::endl;
    
    return 0;
}

注意:赋值后两个 vector 是独立的,修改一个不会影响另一个。

5. 比较运算符

vector 支持所有关系运算符 (==, !=, <, <=, >, >=),比较规则如下:

  • 按元素顺序逐个比较
  • 如果所有元素都相等且大小相同,则 == 返回 true
  • 第一个不相等元素的比较结果决定 <> 的结果

示例

#include <vector>
#include <iostream>

int main() {
    std::vector<int> a = {1, 2, 3};
    std::vector<int> b = {1, 2, 3};
    std::vector<int> c = {1, 2, 4};
    
    std::cout << std::boolalpha;  // 输出 true/false 而非 1/0
    
    std::cout << "a == b: " << (a == b) << std::endl;  // 输出: true
    std::cout << "a == c: " << (a == c) << std::endl;  // 输出: false
    std::cout << "a < c: " << (a < c) << std::endl;   // 输出: true
    std::cout << "a > c: " << (a > c) << std::endl;   // 输出: false
    
    return 0;
}

6. 自定义重载运算符

除了 vector 内置的运算符重载,我们还可以自定义重载其他运算符。

示例1:重载 << 运算符用于输出 vector

#include <vector>
#include <iostream>

// 重载<<运算符
template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& vec) {
    os << "[";
    for (size_t i = 0; i < vec.size(); ++i) {
        os << vec[i];
        if (i != vec.size() - 1) {
            os << ", ";
        }
    }
    os << "]";
    return os;
}

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    
    // 使用重载的<<运算符输出vector
    std::cout << "numbers: " << numbers << std::endl;  // 输出: numbers: [1, 2, 3, 4, 5]
    
    return 0;
}

示例2:重载 + 运算符用于连接两个 vector

#include <vector>
#include <iostream>

// 重载+运算符
template <typename T>
std::vector<T> operator+(const std::vector<T>& a, const std::vector<T>& b) {
    std::vector<T> result = a;
    result.insert(result.end(), b.begin(), b.end());
    return result;
}

int main() {
    std::vector<int> a = {1, 2, 3};
    std::vector<int> b = {4, 5, 6};
    
    // 使用重载的+运算符连接两个vector
    std::vector<int> c = a + b;
    
    // 输出结果
    for (int num : c) {
        std::cout << num << " ";  // 输出: 1 2 3 4 5 6
    }
    std::cout << std::endl;
    
    return 0;
}

7. 安全访问:at() 方法

虽然 [] 运算符很方便,但它不进行边界检查。为了安全访问元素,可以使用 at() 方法:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> numbers = {10, 20, 30};
    
    try {
        // 安全访问,会进行边界检查
        std::cout << numbers.at(5) << std::endl;
    } catch (const std::out_of_range& e) {
        std::cout << "错误: " << e.what() << std::endl;  // 输出: 错误: vector::_M_range_check: __n (which is 5) >= this->size() (which is 3)
    }
    
    return 0;
}

8. 性能考虑

  • []at() 的性能差异:

    • [] 直接访问内存,无额外开销
    • at() 会进行边界检查,有轻微性能损失
  • 赋值运算符 = 的性能:

    • 对于大型 vector,复制操作可能较慢
    • 考虑使用移动语义 (std::move) 避免复制

总结

vector 通过重载运算符提供了类似普通数组的使用方式,同时保留了动态扩容的能力。常用的重载运算符包括:

  • [] 用于元素访问
  • = 用于赋值
  • 关系运算符 (==, !=, <, 等) 用于比较

此外,我们还可以自定义重载其他运算符(如 <<+)以增强 vector 的功能。使用时应注意 []at() 的区别,根据需要选择安全访问或性能优先的方式。

1 条评论

  • @ 2025-5-29 11:59:16

    🧠 C++ STL 容器 std::vector 重载运算符教程

    通俗易懂讲解如何为 vector 中的自定义类型(类或结构体)重载运算符
    📚 涵盖:==, !=, <, >, <<, + 等常用运算符
    🕒 当前时间:2025年5月29日,星期四11时47分


    📘 目录

    1. 🧱 什么是运算符重载?
    2. 🎯 为什么需要为 std::vector 中的元素重载运算符?
    3. 🛠️ 如何为结构体重载常见运算符
      • ==!=
      • <><=>=
      • +- 等数学运算符
      • <<(流输出)
    4. 🧩 在 std::vector 中使用重载后的结构体
    5. 🧪 示例项目:学生信息排序与比较
    6. 🧠 小贴士 & 常见错误
    7. 📌 总结

    1️⃣ 🧱 什么是运算符重载?

    在 C++ 中,你可以通过 运算符重载(Operator Overloading) 来为自定义类型(如结构体、类)定义类似内置类型的运算行为。

    例如:

    Point a(1, 2), b(3, 4);
    Point c = a + b;  // 自定义加法逻辑
    

    2️⃣ 🎯 为什么要为 vector 中的元素重载运算符?

    当你把一个自定义结构体放入 std::vector 后,常常需要:

    • 排序(使用 std::sort)➡️ 需要 < 运算符
    • 查找(使用 std::find)➡️ 需要 == 运算符
    • 输出调试信息 ➡️ 需要 << 流运算符
    • 计算总和、差值等 ➡️ 需要 +, - 运算符

    如果不重载这些运算符,编译器就不知道怎么处理这些操作!


    3️⃣ 🛠️ 如何为结构体重载常见运算符

    🔹 结构体示例

    我们以一个 Student 结构体为例:

    struct Student {
        std::string name;
        int age;
    };
    

    ✅ 1. 重载 ==!=

    用于判断两个对象是否相等。

    bool operator==(const Student& s1, const Student& s2) {
        return s1.name == s2.name && s1.age == s2.age;
    }
    
    bool operator!=(const Student& s1, const Student& s2) {
        return !(s1 == s2);
    }
    

    🔹 使用场景:std::find

    std::vector<Student> students = {{"Alice", 20}, {"Bob", 22}};
    auto it = std::find(students.begin(), students.end(), Student{"Alice", 20});
    

    ✅ 2. 重载 <><=>=

    常用于排序,尤其是 std::sort

    bool operator<(const Student& s1, const Student& s2) {
        if (s1.name != s2.name)
            return s1.name < s2.name;
        return s1.age < s2.age;
    }
    

    🔹 使用场景:std::sort

    std::sort(students.begin(), students.end());
    

    ✅ 3. 重载 + 或其他数学运算符

    例如将两个学生的年龄相加:

    Student operator+(const Student& s1, const Student& s2) {
        return Student{"Combined", s1.age + s2.age};
    }
    

    🔹 使用方式:

    Student sum = students[0] + students[1];
    

    ✅ 4. 重载 <<(输出运算符)

    方便打印调试信息:

    std::ostream& operator<<(std::ostream& os, const Student& s) {
        os << "Student{name: \"" << s.name << "\", age: " << s.age << "}";
        return os;
    }
    

    🔹 使用方式:

    for (const auto& s : students) {
        std::cout << s << std::endl;
    }
    

    4️⃣ 🧩 在 std::vector 中使用重载后的结构体

    ✅ 示例代码汇总:

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <string>
    
    struct Student {
        std::string name;
        int age;
    };
    
    // == 运算符
    bool operator==(const Student& s1, const Student& s2) {
        return s1.name == s2.name && s1.age == s2.age;
    }
    
    // < 运算符(用于排序)
    bool operator<(const Student& s1, const Student& s2) {
        if (s1.name != s2.name)
            return s1.name < s2.name;
        return s1.age < s2.age;
    }
    
    // << 运算符
    std::ostream& operator<<(std::ostream& os, const Student& s) {
        os << "Student{name: \"" << s.name << "\", age: " << s.age << "}";
        return os;
    }
    
    int main() {
        std::vector<Student> students = {
            {"Charlie", 21},
            {"Alice", 20},
            {"Bob", 22}
        };
    
        // 排序
        std::sort(students.begin(), students.end());
    
        // 查找
        Student target{"Alice", 20};
        auto it = std::find(students.begin(), students.end(), target);
    
        if (it != students.end()) {
            std::cout << "Found student: " << *it << std::endl;
        } else {
            std::cout << "Student not found!" << std::endl;
        }
    
        return 0;
    }
    

    5️⃣ 🧪 示例项目:学生信息排序与比较

    你可以在 GitHub 创建一个项目来练习以下内容:

    功能 所需重载
    显示所有学生 <<
    按姓名/年龄排序 <
    判断两个学生是否相同 ==
    求多个学生平均年龄 +/

    6️⃣ 🧠 小贴士 & 常见错误

    推荐做法:

    • 使用 friend 函数进行类内部运算符重载(如果是 class 而不是 struct)。
    • 对于复杂排序,考虑使用 lambda 表达式传入比较函数。
    • 使用 std::tie 简化多重字段比较。

    常见错误:

    • 忘记返回 bool
    • 没有考虑所有字段导致比较错误
    • 混淆成员函数与非成员函数(如 operator<< 必须是非成员函数)

    7️⃣ 📌 总结

    运算符 用途 是否必须
    == / != 比较、查找
    < / > 排序、集合容器
    << 输出调试信息 ❗建议
    + / - 数学计算 可选

    📄 下一步学习建议

    • 学习 std::mapstd::set 的使用(依赖 < 运算符)
    • 探索模板泛型下的运算符重载
    • 学习智能指针与自定义容器结合使用
    • 实践设计模式(如工厂模式创建 vector 元素)
    • 1