15

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

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

这个类的用户需要调用一个 API 函数(比如,一个我无法控制的函数,所以我不能只是改变它的接口来让事情更容易工作),看起来像这样:

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

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

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

这是做事的唯一方法吗?我对像这样声明的多维数组是多么不灵活感到震惊。我认为matrix_基本上与 a 相同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

错误是:

错误 C2440:“类型转换”:无法从“void *”转换为“const double [4][4]”

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

没有对数组类型的转换,尽管有对引用或指向数组的指针的转换

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

可以肯定的是,在这一点上,这件事纯粹是学术性的,因为void*演员阵容几乎不比一个公开的班级成员更干净!

4

5 回答 5

16

这是一个很好,干净的方式:

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

   // ...

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

   // ...

private:
  matrix matrix_;
};

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

于 2008-09-10T19:53:16.443 回答
6

试试这个。它在 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;
}
于 2008-09-10T19:59:33.167 回答
4

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

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

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

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

于 2008-09-10T19:38:02.207 回答
4

我认为 matrix_ 本质上与双精度相同**

在 C 中有真正的多维数组,而不是指向数组的指针数组,所以一个 double[4][4] 是一个由四个 double[4] 数组组成的连续数组,相当于一个 double[16],而不是一个 (double *)[4]。

没有对数组类型的转换,尽管有对数组的引用或指针的转换将值转换为 double[4][4] 将尝试在堆栈上构造一个 - 相当于 std::string(parser.getMatrix( )) - 除了数组不提供合适的构造函数。即使可以,您也可能不想这样做。

由于类型对步幅进行编码,因此您需要一个完整类型(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();
}
于 2008-09-10T20:07:17.347 回答
2

要详细说明所选答案,请注意这一行

const matrix& getMatrix() const

这很棒,您不必担心指针和强制转换。您正在返回对基础矩阵对象的引用。恕我直言,引用是 C++ 的最佳特性之一,我在直接用 C 编码时错过了它。

如果您不熟悉 C++ 中引用和指针之间的区别,请阅读本文

无论如何,您必须意识到,如果Parser实际拥有底层矩阵对象的对象超出范围,则任何尝试通过该引用访问矩阵的代码现在都将引用一个超出范围的对象,并且你会崩溃的。

于 2008-09-10T21:15:18.540 回答