C++ 下标运算符 [] 重载(完整教程)

C++ 下标运算符 [] 重载:让对象像数组一样使用

在 C++ 中,我们常常希望自定义的类能够像内置类型一样使用下标运算符 [] 来访问内部数据。比如,当你定义一个 MyArray 类来封装一个动态数组时,自然希望可以这样写:myArray[3] 来获取第 4 个元素。这正是 C++ 下标运算符 [] 重载 的核心价值所在。

想象一下,你有一个“智能盒子”,它内部藏了多个数据。如果你不重载 [],你就得用 get(3) 这样的方法去取值,显得笨重。但一旦重载了 [],你就可以像操作普通数组一样,用 box[3] 来直接访问,代码简洁、直观,也更符合直觉。


为什么需要下标运算符重载?

在标准 C++ 中,[] 是一种内置运算符,专用于数组访问。但当我们创建自己的类,比如一个容器类、矩阵类或字符串类时,内部数据通常用 std::vectorstd::array 或原生指针管理。此时,如果不提供 [] 的重载,就无法像数组一样使用。

举个例子:

class MyString {
private:
    char* data;
    size_t size;

public:
    MyString(const char* str) {
        size = strlen(str);
        data = new char[size + 1];
        strcpy(data, str);
    }

    // 没有重载 [],无法用 str[3] 访问
};

此时,你只能用 str.at(3)str.getData()[3] 这类方式,显得不自然。而通过重载 [],我们就能让类的行为更“原生”。


基本语法与声明方式

[] 运算符重载必须是类的成员函数,且只能是成员函数。它接受一个参数,通常是 size_t 类型的索引值。

重载语法格式:

返回类型 operator[](参数类型 索引) {
    // 实现逻辑
}

注意:

  • [] 不能是静态成员函数
  • 不能重载全局版本(即不能在类外定义 operator[]
  • 参数类型通常为 size_tint,但推荐使用 size_t 以避免负数索引

重载的两个版本:const 与非 const

这是 [] 重载最核心的技巧之一。我们通常需要两个版本:

  1. 非 const 版本:用于赋值和修改(如 arr[2] = 10
  2. const 版本:用于只读访问(如 cout << arr[2]

示例代码:

class MyArray {
private:
    int* data;
    size_t length;

public:
    MyArray(size_t n) : length(n) {
        data = new int[length];
        for (size_t i = 0; i < length; ++i) {
            data[i] = 0; // 初始化为 0
        }
    }

    // 非 const 版本:允许修改
    int& operator[](size_t index) {
        // 检查索引是否越界
        if (index >= length) {
            throw std::out_of_range("索引越界");
        }
        return data[index]; // 返回引用,允许赋值
    }

    // const 版本:只读访问
    const int& operator[](size_t index) const {
        if (index >= length) {
            throw std::out_of_range("索引越界");
        }
        return data[index]; // 返回 const 引用,防止修改
    }

    ~MyArray() {
        delete[] data;
    }
};

关键点说明:

  • 非 const 版本返回 int&(引用),这样 arr[2] = 5 才能生效
  • const 版本返回 const int&,防止在 const MyArray 对象上调用时被修改
  • 两个函数构成重载,根据调用对象是否 const 自动选择

实际使用案例:自定义动态数组类

让我们构建一个更完整的 DynamicArray 类,支持 [] 重载,并加入自动扩容机制。

#include <iostream>
#include <stdexcept>
#include <algorithm>

class DynamicArray {
private:
    int* data;
    size_t size;
    size_t capacity;

    // 扩容函数
    void resize() {
        size_t new_capacity = std::max(2 * capacity, 1ul);
        int* new_data = new int[new_capacity];
        std::copy(data, data + size, new_data);
        delete[] data;
        data = new_data;
        capacity = new_capacity;
    }

public:
    DynamicArray() : size(0), capacity(1) {
        data = new int[capacity];
    }

    // 拷贝构造函数
    DynamicArray(const DynamicArray& other)
        : size(other.size), capacity(other.capacity) {
        data = new int[capacity];
        std::copy(other.data, other.data + size, data);
    }

    // 析构函数
    ~DynamicArray() {
        delete[] data;
    }

    // 重载 []:非 const 版本
    int& operator[](size_t index) {
        if (index >= size) {
            // 如果索引超出当前大小,自动扩容
            while (index >= capacity) {
                resize();
            }
            size = index + 1;
        }
        return data[index];
    }

    // 重载 []:const 版本
    const int& operator[](size_t index) const {
        if (index >= size) {
            throw std::out_of_range("索引越界");
        }
        return data[index];
    }

    // 获取当前大小
    size_t getSize() const {
        return size;
    }

    // 输出数组内容
    void print() const {
        for (size_t i = 0; i < size; ++i) {
            std::cout << data[i] << " ";
        }
        std::cout << std::endl;
    }
};

使用示例:

int main() {
    DynamicArray arr;

    // 使用 [] 直接赋值,自动扩容
    arr[0] = 10;
    arr[2] = 30;  // 索引为 2,自动扩容到容量 4,size 变为 3
    arr[5] = 50;  // 索引 5,自动扩容,size 变为 6

    std::cout << "数组内容: ";
    arr.print();  // 输出: 10 0 30 0 0 50

    // const 对象只能读取
    const DynamicArray& const_arr = arr;
    std::cout << "第 2 个元素: " << const_arr[2] << std::endl; // 输出 30

    return 0;
}

运行结果:

数组内容: 10 0 30 0 0 50
第 2 个元素: 30

这个例子展示了 C++ 下标运算符 [] 重载 的强大之处:让自定义容器拥有原生数组的使用体验。


常见陷阱与最佳实践

1. 忘记处理越界问题

很多初学者只写 return data[index];,但没有检查索引是否合法。这会导致程序崩溃。

✅ 正确做法:始终检查 index < size,并抛出 std::out_of_range 异常。

2. 返回值类型错误

  • 修改时:必须返回 类型&(引用),否则 arr[2] = 5 无效
  • 只读时:应返回 const 类型&,防止误修改

3. 没有提供 const 版本

如果类中某些方法是 const 的,但你没有定义 const operator[],编译器会报错。

4. 内存管理不当

如果 [] 重载中涉及动态内存,一定要保证内存安全,避免内存泄漏或悬空指针。


总结与进阶建议

C++ 下标运算符 [] 重载 是提升类可读性和可用性的重要手段。它让自定义类“看起来像数组”,极大增强了代码的表达力。

  • 重载必须是成员函数
  • 必须提供 const 与非 const 两个版本
  • 返回引用才能支持赋值操作
  • 建议加入边界检查,提升程序健壮性
  • 适合用于容器类、矩阵、字符串、自定义数组等场景

当你在项目中频繁使用“索引访问”时,不妨思考一下:是否该重载 []?这不仅能让你的代码更优雅,也能让其他开发者更容易理解你的类设计。

记住,好的 API 不只是功能正确,更要“直觉自然”。[] 运算符重载,正是实现这一目标的关键工具之一。