题
我有一个类来解析矩阵,将结果保存在数组成员中:
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
错误是:
错误 C2440:'类型转换':无法从“void *”转换为“const double [4][4]”
...有一个有趣的附录:
尽管存在对数组的引用或指针的转换,但没有对数组类型的转换
我也无法确定如何转换为引用或指向数组的指针,尽管它可能对我没有帮助。
可以肯定的是,目前这个问题纯粹是学术性的,因为 void*
演员阵容并不比公开的单个班级成员干净!
解决方案
这是一个很好、干净的方法:
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;
}
我过去曾使用这样的联合来传递矩阵:
union matrix {
double dflat[16];
double dmatr[4][4];
};
然后将指针传递给您的设置器并将数据复制到您的类中的矩阵中。
有一些方法可以处理这个问题(更通用),但根据我的经验,这个解决方案最终往往是最干净的。
我认为matrix_本质上与double**相同
在 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();
}
要详细说明所选答案,请观察这一行
const matrix& getMatrix() const
这太棒了,您不必担心指针和转换。您正在返回一个 参考 到底层矩阵对象。恕我直言,引用是 C++ 的最佳功能之一,在直接使用 C 进行编码时我很怀念这一点。
如果您不熟悉 C++ 中引用和指针之间的区别, 读这个
无论如何,您必须意识到,如果 Parser
实际拥有底层矩阵对象的对象超出了范围,任何尝试通过该引用访问矩阵的代码现在都将引用超出范围的对象,并且您将崩溃。