Как работать с массивами (объявленными в стеке) на С++?
У меня есть класс для разбора матрицы, которая сохраняет результат в элементе массива:
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_;
}
Это скомпилируется, но я не могу его использовать, потому что, похоже, не существует способа вернуть массив типов weirdo:
// 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 to the wrong type
Ошибка:
error C2440: 'type cast': не может преобразовать из 'void *' в 'const double [4] [4]'
... с интригующим добавлением:
Нет никаких преобразований в типы массивов, хотя есть ссылки на ссылки или указатели на массивы
Я не могу определить, как отбрасывать ссылку или указатель на массив, хотя это, вероятно, мне не поможет.
Конечно, на данный момент вопрос носит чисто академический характер, поскольку приведения void*
вряд ли более чисты, чем один член класса, оставленный публичным!
Ответы
Ответ 1
Здесь хороший, чистый способ:
class Parser
{
public:
typedef double matrix[4][4];
// ...
const matrix& getMatrix() const
{
return matrix_;
}
// ...
private:
matrix matrix_;
};
Теперь вы работаете с дескриптивным именем типа, а не с массивом, но поскольку он typedef
, компилятор все равно позволит передать его неизменной функции API, которая берет базовый тип.
Ответ 2
Попробуйте это. Он компилируется на 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;
}
Ответ 3
Я использовал союз, подобный этому, чтобы передать матрицы в прошлом:
union matrix {
double dflat[16];
double dmatr[4][4];
};
Затем передайте указатель в свой сеттер и скопируйте данные в матрицу вашего класса.
Есть способы обращения с этим в противном случае (более общие), но это решение, как мне кажется, является самым чистым в конце.
Ответ 4
Я думал, что матрица будет по существу такой же, как двойная **
В C есть истинные многомерные массивы, а не массивы указателей на массивы, поэтому двойной [4] [4] является смежным массивом из четырех двойных [4] массивов, эквивалентным двойному [16], а не a (double *) [4].
Нет никаких преобразований в типы массивов, хотя есть ссылки на ссылки или указатели на массивы Приведение значения к double [4] [4] попытается построить один в стеке, эквивалентный std::string (parser.getMatrix()), за исключением того, что массив не предоставляет подходящий конструктор. Вы, вероятно, не хотели этого делать, даже если бы могли.
Так как тип кодирует шаг, вам нужен полный тип (double [] [] не будет делать). Вы можете переинтерпретировать листинг void * ((double [4] [4]) *), а затем взять ссылку. Но проще всего набрать матрицу и вначале вернуть ссылку правильного типа:
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();
}
Ответ 5
Чтобы уточнить выбранный ответ, обратите внимание на эту строку
const matrix& getMatrix() const
Это здорово, вам не нужно беспокоиться о указателях и кастингах. Вы возвращаете ссылку на базовый объект-матрицу. ИМХО-ссылки - одна из лучших особенностей С++, которую я пропускаю при кодировании в прямом C.
Если вы не знакомы с разницей между ссылками и указателями на С++, прочитайте это
В любом случае вам нужно знать, что если объект Parser
, который фактически владеет базовым объектом матрицы, выходит за пределы области видимости, любой код, который пытается получить доступ к матрице через эту ссылку, теперь будет ссылаться на out- объекта scope, и вы потерпите крах.