使用[]下标操作符,我们可像数组下标访问形式一样访问对象。这种操作方式简单而且形象,又方便理解。
注解:operator[]下标操作符只能作为类的成员函数。
重载operator[]也没我们想象的那么简单。在重载operator[]时,你必须保证他在用作赋值的左右操作数是都能够正常工作,如果下标操作符出现在左边,它可生成左值;如果下标操作符出现在右边它可生产右值。符合这两种假设,operator[]操作符只能使用返回值。
操作符返回引用可以用在赋值的任何一方;除此之外,还需要考虑const对象和非const对象的差异。const对象只能访问const成员函数,而且const对象不允许被修改。应用于const对象时,operator[]应返回const引用,因为不能用在赋值的目标。
最佳实践:定义类的下标操作符时,一般应定义两个版本:一个为非const成员并返回引用。一个为const成员并返回const引用。
来看一个下标操作符重载的例子:
// 数据联合类实现。
class CItems
{
public:
CItem &operator[](const int iIndex);
const CItem &operator[](const int iIndex) const ;
private:
std::vector<CItem> m_Items;
}
//左值 [] 操作符重载
CItem &CItems::operator[](const int iIndex)
{
return m_Items[iIndex]; // 没有进行范围检查。
}
// 右值[] 操作符重载
const CItem &CItems::operator[](const int iIndex) const
{
return m_Items[iIndex]; // 没有进行范围检查。
}
这段代码关于operator[]的实现依然存在下标越界问题。那么应如何避免呢?就从这两个重载函数而言,我们无法避免这类问题的出现。因为operator[]只接受一个参数,即下标,所以无法得知传入的下标值是否合法。
但是是否有其他的解决方案呢?比如发现传入的下标非法就立刻抛出异常。我们看这种假设的实现。
CItem &CItems::operator[](const int iIndex)
{
if ( iIndex < m_Items.size())
{
return m_Items[iIndex]; // 没有进行范围检查。
}
else
{
throw(); // 抛出一个异常,明确的告诉开发者,这儿传入的下标值非法了。
}
}
const CItem &CItems::operator[](const int iIndex) const
{
if ( iIndex < m_Items.size())
{
return m_Items[iIndex]; // 没有进行范围检查。
}
else
{
throw(); // 抛出一个异常,明确的告诉开发者,这儿传入的下标值非法了。
}
}
以程序停止运行为代价,提示存在问题,确实有一定的可取之处。但是有时候这种代价未免太大了。如果你的程序运行每天会给客户提供上千万元的效益,然而有一天它突然停止运行了。那么后果可想而知。
还有更好的解决方法吗?答案是有的。有更安全的做法。这种作为其实更简单。就是在每次调用operator[]之前,判断下标值是否合法。
class CItems
{
public:
CItem &operator[](const int iIndex);
const CItem &operator[](const int iIndex) const ;
int GetMaxIndex();
private:
std::vector<CItem> m_Items;
}
int CItems::GetMaxIndex()
{
return m_Items.size();
}
// 使用下标操作符号
int iIndex = 0;
CItems* pItems = new CItems();
…
// 检查下标值是否合法。只有合法的情形下才执行operator[]操作。
if (iIndex < pItems-> GetMaxIndex)
{
CItem *pItem = pItems[iIndex];
}
两种方案对比
第一种方案:如果下标值越界了,抛出异常。这种方案可在调试过程中使用。在软件发布阶段不适合使用,因为一旦软件抛出异常。客户会为此付出惨痛的代价。
第二种方案:每次通过下标操作时,首先检查下标是否越界。只有在非越界的情况下,才执行下标重载函数。这种方法和第一种方法相比比较安全。只要使用得当,软件不会抛出异常。
请谨记
- 请注意operator[]下标操作符只能作为类的成员函数。同时在定义类的下标操作符时请定义两个版本。以适应const对象和非const对象。
- 小心operator[]带来的陷阱。在重载时必须把这部分陷阱考虑进去,以防为此付出惨痛的代价。