如何在C ++中处理数组(在堆栈上声明)?

时间:2020-03-05 18:51:01  来源:igfitidea点击:

我有一个类来解析将结果保留在数组成员中的矩阵:

class Parser
{
  ...
  double matrix_[4][4];
};

此类的用户需要调用如下所示的API函数(例如,我无法控制的函数,因此我不能仅更改其接口以使事情更轻松地工作):

void api_func(const double matrix[4][4]);

我想出的让调用者将数组结果传递给函数的唯一方法是使成员成为公共成员:

void myfunc()
{
  Parser parser;
  ...
  api_func(parser.matrix_);
}

这是做事情的唯一方法吗?我对这样声明的不灵活的多维数组感到惊讶。我认为matrix_本质上与double **相同,并且我可以(安全地)在两者之间进行转换。事实证明,我什至找不到一种在事物之间进行转换的不安全方法。假设我在Parser类中添加了一个访问器:

void* Parser::getMatrix()
{
  return (void*)matrix_;
}

它将编译,但是我不能使用它,因为似乎没有一种方法可以转换回怪异数组类型:

// A smorgasbord of syntax errors...
  api_func((double[][])parser.getMatrix());
  api_func((double[4][4])parser.getMatrix());
  api_func((double**)parser.getMatrix()); // cast works but it's to the wrong type

错误是:

error C2440: 'type cast' : cannot convert from 'void *' to 'const double [4][4]'

...带有一个有趣的附录:

There are no conversions to array types, although there are conversions to references or pointers to arrays

我也无法确定如何转换为引用或者数组指针,尽管这可能对我没有帮助。

可以肯定的是,在这一点上这纯粹是学术问题,因为" void *"演员表几乎没有一个公开的集体成员干净!

解决方案

回答

过去,我曾使用过像这样的联合来传递矩阵:

union matrix {
    double dflat[16];
    double dmatr[4][4];
};

然后将指针传递到设置器中,然后将数据复制到类中的矩阵中。

有其他方法可以解决此问题(更为通用),但是根据我的经验,此解决方案最终往往是最干净的。

回答

这是一种不错的方法:

class Parser
{
public:
   typedef double matrix[4][4];

   // ...

   const matrix& getMatrix() const
   {
      return matrix_;
   }

   // ...

private:
  matrix matrix_;
};

现在,我们正在使用描述性类型名称而不是数组,但是由于它是typedef,因此编译器仍允许将其传递给采用基本类型的不可更改的API函数。

回答

试试这个。它可以在gcc 4.1.3上干净地编译:

typedef double FourSquare[4][4];

class Parser
{
  private:
    double matrix_[4][4];

  public:
    Parser()
    {
        for(int i=0; i<4; i++)
          for(int j=0; j<4; j++)
            matrix_[i][j] = i*j;
    }

  public:
    const FourSquare& GetMatrix()
    {
        return matrix_;
    }
};

void api_func( const double matrix[4][4] )
{
}

int main( int argc, char** argv )
{
    Parser parser;
    api_func( parser.GetMatrix() );
    return 0;
}

回答

I thought matrix_ would essentially be the same as a double**

在C语言中,存在真正的多维数组,而不是指向数组的指针的数组,因此double [4] [4]是四个double [4]数组的连续数组,等效于double [16],而不是(double *)[4]。

There are no conversions to array types, although there are conversions to references or pointers to arrays
  Casting a value to a double[4][4] would attempt to construct one on the stack  - equivalent to std::string(parser.getMatrix()) - except that the array doesn't supply a suitable constructor. You probably did't want to do that, even if you could.

由于类型编码跨步,因此需要完整类型(不会使用double [] [])。我们可以将void 重新解释为((double [4] [4])),然后引用。但是最简单的方法是首先对矩阵进行typedef定义,然后首先返回正确类型的引用:

typedef double matrix_t[4][4];

class Parser
{
    double matrix_[4][4];
public:
    void* get_matrix () { return static_cast<void*>(matrix_); }

    const matrix_t& get_matrix_ref () const { return matrix_; }
};

int main ()
{
    Parser p;

    matrix_t& data1 = *reinterpret_cast<matrix_t*>(p.get_matrix());

    const matrix_t& data2 = p.get_matrix_ref();
}

回答

要详细说明选定的答案,请注意以下一行

const matrix& getMatrix() const

太好了,我们不必担心指针和强制转换。我们正在返回对基础矩阵对象的引用。恕我直言,引用是C ++的最佳功能之一,当我用纯C语言编写代码时我会想念它。

如果我们不熟悉C ++中的引用和指针之间的区别,请阅读以下内容

无论如何,我们一定要知道,如果实际上拥有底层矩阵对象的Parser对象超出范围,那么任何试图通过该引用访问矩阵的代码现在都将引用范围外对象,我们将崩溃。