简体   繁体   English

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

[英]How to deal with arrays (declared on the stack) in C++?

I have a class to parse a matrix that keeps the result in an array member: 我有一个类来解析一个矩阵,将结果保存在数组成员中:

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

The user of this class needs to call an API function (as in, a function I have no control over, so I can't just change its interface to make things work more easily) that looks like this: 这个类的用户需要调用一个API函数(例如,我无法控制的函数,所以我不能只是改变它的界面以使事情更容易工作),如下所示:

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

The only way I have come up with for the caller to pass the array result to the function is by making the member public: 我让调用者将数组结果传递给函数的唯一方法是将成员公开:

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

Is this the only way to do things? 这是做事的唯一方法吗? I'm astounded by how inflexible multidimensional arrays declared like this are. 我对这样声明的多维数组的不灵活性感到震惊。 I thought matrix_ would essentially be the same as a double** and I could cast (safely) between the two. 我认为matrix_基本上与double**相同,我可以(安全地)在两者之间施放。 As it turns out, I can't even find an unsafe way to cast between the things. 事实证明,我甚至找不到一种不安全的方式在事物之间施放。 Say I add an accessor to the Parser class: 假设我添加了一个Parser类的访问器:

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

This will compile, but I can't use it, because there doesn't seem to be a way to cast back to the weirdo array type: 这将编译,但我不能使用它,因为似乎没有办法转回到怪人数组类型:

  // 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

The error is: 错误是:

error C2440: 'type cast' : cannot convert from 'void *' to 'const double [4][4]' 错误C2440:'type cast':无法从'void *'转换为'const double [4] [4]'

...with an intriguing addendum: ......有一个有趣的附录:

There are no conversions to array types, although there are conversions to references or pointers to arrays 虽然存在对引用或指向数组的指针的转换,但是没有对数组类型的转换

I can't determine how to cast to a reference or pointer to array either, albeit that it probably won't help me here. 我无法确定如何转换为引用或指向数组的指针,尽管它可能对我没有帮助。

To be sure, at this point the matter is purely academic, as the void* casts are hardly cleaner than a single class member left public! 可以肯定的是,在这一点上,这个问题纯粹是学术性的,因为void*演员阵容并不比一个单独的集体成员更公开!

Here's a nice, clean way: 这是一个很好,干净的方式:

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

   // ...

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

   // ...

private:
  matrix matrix_;
};

Now you're working with a descriptive type name rather than an array, but since it's a typedef the compiler will still allow passing it to the unchangeable API function that takes the base type. 现在您正在使用描述性类型名称而不是数组,但由于它是一个typedef ,编译器仍然允许将其传递给采用基本类型的不可更改的API函数。

Try this. 试试这个。 It compiles cleanly on gcc 4.1.3: 它在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've used a union like this to pass around matrices in the past: 我曾经使用像这样的联盟来传递过去的矩阵:

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

Then pass a pointer in to your setter and copy the data into the matrix in your class. 然后将指针传递给setter并将数据复制到类中的矩阵中。

There are ways of handling this otherwise (that are more generic), but this solution tends to be the cleanest in the end, in my experience. 有办法处理这个问题(更通用),但根据我的经验,这个解决方案往往是最干净的。

I thought matrix_ would essentially be the same as a double** 我认为matrix_基本上和双**一样

In C there are true multi-dimensional arrays, not arrays of pointers to arrays, so a double[4][4] is a contiguous array of four double[4] arrays, equivalent to a double[16], not a (double*)[4]. 在C中有真正的多维数组,而不是指向数组的指针数组,因此double [4] [4]是四个double [4]数组的连续数组,相当于double [16],而不是a(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. 没有对数组类型的转换,尽管有引用转换或指向数组的指针将值转换为double [4] [4]会尝试在堆栈上构造一个 - 相当于std :: string(parser.getMatrix( )) - 除了数组不提供合适的构造函数。 You probably did't want to do that, even if you could. 你可能不想这样做,即使你可以。

Since the type encodes the stride, you need a full type (double[][] won't do). 由于类型对步幅进行编码,因此需要完整类型(double [] []不会这样做)。 You can reinterpret cast the void* to ((double[4][4])*), and then take the reference. 您可以重新解释将void *转换为((d​​ouble [4] [4])*),然后获取引用。 But it's easiest to typedef the matrix and return a reference of the correct type in the first place: 但是最简单的方法是输入矩阵并首先返回正确类型的引用:

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();
}

To elaborate on the selected answer, observe this line 要详细说明所选答案,请遵守此行

const matrix& getMatrix() const

This is great, you don't have to worry about pointers and casting. 这很棒,你不必担心指针和铸造。 You're returning a reference to the underlying matrix object. 您将返回对基础矩阵对象的引用 IMHO references are one of the best features of C++, which I miss when coding in straight C. 恕我直言参考是C ++的最佳功能之一,我在直接C编码时错过了。

If you're not familiar with the difference between references and pointers in C++, read this 如果您不熟悉C ++中引用和指针之间的区别, 请阅读此内容

At any rate, you do have to be aware that if the Parser object which actually owns the underlying matrix object goes out of scope, any code which tries to access the matrix via that reference will now be referencing an out-of-scope object, and you'll crash. 无论如何,你必须要注意,如果实际拥有底层矩阵对象的Parser对象超出范围,那么任何试图通过该引用访问矩阵的代码现在将引用一个超出范围的对象,你会崩溃

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM