operator[]必须返回引用才能支持赋值,应同时提供t&和const t&两个重载;越界检查依场景选择:性能敏感时可不检查或仅debug用assert,否则用异常或panic。
![c++如何优雅地重载数组下标操作符operator[]](https://img.php.cn/upload/article/001/221/864/179012906822254.png?x-oss-process=image/resize,p_40)
operator[] 必须返回引用才能支持赋值
很多人写 operator[] 时直接返回 int 或 T,结果发现 a[0] = 42; 编译失败——因为右值不能被赋值。正确做法是返回 T&(非常量版本)和 const T&(常量版本),让表达式既能读也能写。
常见错误示例:
int operator[](size_t i) { return data[i]; } // ❌ 返回值类型导致 a[i] = x; 失败
应改为:
T& operator[](size_t i) { return data[i]; }<br>const T& operator[](size_t i) const { return data[i]; }
注意:两个重载必须同时存在,否则 const MyArray a; a[0]; 会因找不到匹配函数而报错 no matching function for call to 'MyArray::operator[]'。
越界检查要不要做?用 assert 还是异常?
标准库如 std::vector::operator[] 不检查越界,而 at() 检查并抛异常。你自己的类要不要加检查,取决于使用场景:
- 追求性能且调用方可信(如内部算法层)→ 不检查,或仅在 debug 模式用
assert(i - 暴露给外部用户或脚本层 → 建议抛
std::out_of_range,错误信息里带上索引和 size,方便定位 - 嵌入式或无异常环境 → 返回引用前用
if (i >= size_) abort();或自定义 panic
别用 std::cerr 这种“静默兜底”,它掩盖问题且行为不可预测。
代理对象(proxy)适用于哪些特殊场景?
当你的“数组”语义不是简单内存访问时,比如:
- 位级访问(
BitArray[10]返回一个能当 bool 用又能被赋值的代理) - 稀疏数组(
SparseArray[i]访问不存在的索引时自动构造默认值) - 只读视图包装(返回 proxy 阻止对底层数据的意外修改)
这时直接返回 T& 不够用,得返回一个轻量代理类,重载其 operator T(隐式转目标类型)和 operator=(写入逻辑)。但注意:proxy 会抑制返回值优化、增加调用开销,普通连续内存容器没必要上。
典型签名:
Proxy operator[](size_t i) { return Proxy(*this, i); }<br>const Proxy operator[](size_t i) const { return Proxy(*this, i); }
迭代器兼容性:operator[] 和 at() 的分工要清晰
operator[] 的语义就是“不检查、不安全、快”,这是它和 at() 的根本区别。如果你在 operator[] 里加了越界检查并抛异常,就违背了 STL 的约定,会让熟悉标准库的人困惑。
用户代码可能这样写:
for (size_t i = 0; i <p>所以:<code>operator[]</code> 只做下标到地址的映射;所有边界/空值/懒初始化逻辑,都该放进 <code>at()</code>、<code>front()</code>、<code>back()</code> 等辅助接口里。</p> <p>另外,别为了省一行代码把 <code>operator[]</code> 写成 <code>return at(i);</code> —— 这会让本来想跳过检查的用户被迫承受额外开销。</p>
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











