【问题标题】:Overload index operator to mimic POD multi-dimensional array?重载索引运算符以模仿 POD 多维数组?
【发布时间】:2015-07-02 14:05:38
【问题描述】:

我有一个现有的课程,其结构如下:

class Matrix
{
public:
    float Data[4][4];
    // ... methods
};

并随后用于以下方式:

Matrix m;
m.Data[0][0] = 1.0f;
float local = m.Data[0][0];
//...

我想用重载的索引运算符替换Data 成员,这样我就可以对使用的索引执行范围检查。虽然我可以更改 Matrix 类本身及其成员函数的实现,但我无法修改其在现有代码中的用法,因此任何解决方案都要求用法语法保持相同。还希望解决方案不会改变sizeof(Matrix)。有没有办法做到这一点?

【问题讨论】:

  • 你需要多级间接,每个类只能重载一个索引运算符。
  • 是的,我明白 - 但是,由于已知它是一个二维数组,Matrix 的索引运算符可以返回某种接口,它也可以覆盖它的索引运算符? ...如果实际上可能的话,不确定它的结构。
  • 顺便说一句,您希望如何实际存储数据?如果每一行在内存中仍然是连续的,你可以重载一个operator[]来返回float*---行开始的地址---第二个[]会被自动处理。
  • @Petr 问题是第二个索引的范围检查。
  • 我不确定您要的是什么,您希望它以完全相同的方式使用:m.Data[][],而Data 将是一个带有operator[] 重载的结构?如果有,有什么用?

标签: c++ arrays operator-overloading


【解决方案1】:

一般这种事情,你需要使用代理类来处理第二个索引操作符。它看起来像这样并进入Matrix 类的private 部分。我将省略边界检查(您自己添加它应该不难)。

class Proxy {
    Matrix& ref;
    size_t i;
public:
    Proxy(Matrix& on, size_t i) : ref(on), i(i) {}
    float operator[] (size_t j) {
        return ref.Data[i][j];
    }
};

然后让您的 Matrix::operator[] 返回此类的一个实例:

Proxy operator[] (size_t i) {
    return Proxy(*this, i);
}

请注意,如果您想要一个 const 重载(即,您想要在 const Matrix 对象上使用索引运算符),您将需要一个单独的 ConstProxy 类,该类具有 const Matrix& ref 而不是 Matrix& ref 但在其他方面是相同的.


还可以选择返回对数组的引用。 (注意:正如其中一位 cmets 所指出的,这对边界检查没有多大帮助,但我认为这很有趣,所以我将其留在这里。)

float (&operator[](size_t i))[4] {
    return Data[i];
}

它的语法相当晦涩,我相信它在 Visual Studio 2013 中不起作用,但您可以使用 typedef 使其更简洁。

using Proxy = float[4];
Proxy& operator[](size_t i) {
    return Data[i];
}

如果您不介意放弃方括号索引,还有另一种选择。您可以像这样重载函数调用运算符:

float operator()(size_t i, size_t j) {
    return Data[i][j];
}

【讨论】:

  • 对于第二种语法,你为什么不直接返回float*?无论如何,它对边界检查没有帮助。
  • 通过返回数组引用,记住数组的大小。这意味着您可以执行类似for(float n : my_matrix[i]) 的操作,例如。如果返回float*,则大小丢失。
  • 另外,使用引用可以更好地推广到高维数组。
  • 这里的部分复杂性是我需要重载的并不是真正的Matrix::operator[],它实际上是Data 对象本身,所以这不太正确。
  • 那么你需要一个使用相同技术的新类(例如,我们称之为MatrixData)。然后你会声明 Data 属于那个类 - MatrixData Data;。虽然这是一个不必要的间接级别,所以如果可能的话,我建议像描述的那样重载Matrix::operator[],而不是通过箍来仍然能够使用m.Data[i][j]语法访问。
【解决方案2】:

另一种方式是定义一个具有向量语义的代理类

 class Matrix
{
public:
    struct SubMatrix
    {
        class Vector
        {
        public:
            Vector(float *data) : Data(data) {}
            float &operator[](int index) { return Data[index]; }
        private:
            float *Data;
        };
        Vector operator[](int index)
        {
            return Vector(Data[index]);
        }
        float Data[4][4];
    };
    SubMatrix Data;
    // ... methods
};

那么你可以这样使用它:

Matrix m;
float f = m.Data[1][2];

【讨论】:

  • 与其他答案一样,这需要更改语法 - m[1][2] 而不是 m.Data[1][2]
  • @MuertoExcobito 这样可以保持语法
  • 但是是什么也阻止了您更改该代码?在任何情况下,如果您真的愿意,您仍然可以使用我或 marom 描述的相同技术,同时保持您的 .Data 语法。
  • @celticminstrel - 并非所有代码都在我的控制之下。我已经接受了这个答案,我认为它提供了最简洁的解决方案。谢谢。
猜你喜欢
  • 1970-01-01
  • 2013-05-15
  • 2019-08-21
  • 1970-01-01
  • 1970-01-01
  • 2016-03-31
  • 1970-01-01
  • 2021-08-01
  • 2010-12-30
相关资源
最近更新 更多