Pregunta

Tengo una clase para analizar una matriz que mantiene el resultado en un miembro de la matriz:

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

El usuario de esta clase necesita llamar a una función API (como en una función sobre la que no tengo control, por lo que no puedo simplemente cambiar su interfaz para que las cosas funcionen más fácilmente) que se ve así:

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

La única forma que se me ocurrió para que la persona que llama pase el resultado de la matriz a la función es haciendo público el miembro:

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

¿Es esta la única manera de hacer las cosas?Me sorprende lo inflexibles que son las matrices multidimensionales declaradas como esta.Pensé matrix_ esencialmente sería lo mismo que un double** y pude lanzar (con seguridad) entre los dos.Resulta que ni siquiera puedo encontrar un inseguro manera de lanzar entre las cosas.Digamos que agrego un descriptor de acceso al Parser clase:

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

Esto se compilará, pero no puedo usarlo, porque no parece haber una manera de volver al tipo de matriz extraño:

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

El error es:

error C2440:'tipo de reparto':no se puede convertir de 'void *' a 'const double [4][4]'

...con un apéndice intrigante:

No hay conversiones a tipos de matrices, aunque sí a referencias o punteros a matrices.

Tampoco puedo determinar cómo convertir una referencia o un puntero a una matriz, aunque probablemente no me ayude aquí.

Sin duda, a estas alturas la cuestión es puramente académica, ya que la void* ¡Los elencos no son más limpios que los que un solo miembro de la clase dejó en público!

¿Fue útil?

Solución

Aquí tienes una forma bonita y limpia:

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

   // ...

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

   // ...

private:
  matrix matrix_;
};

Ahora estás trabajando con un nombre de tipo descriptivo en lugar de una matriz, pero como es un typedef el compilador aún permitirá pasarlo a la función API inmutable que toma el tipo base.

Otros consejos

Prueba esto.Se compila limpiamente en 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;
}

He usado una unión como esta para pasar matrices en el pasado:

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

Luego pase un puntero a su configurador y copie los datos en la matriz de su clase.

Hay formas de manejar esto de otra manera (que son más genéricas), pero, en mi experiencia, esta solución tiende a ser la más limpia al final.

Pensé que Matrix_ sería esencialmente lo mismo que un doble**

En C hay verdaderos arreglos multidimensionales, no arreglos de punteros a arreglos, por lo que un doble[4][4] es un arreglo contiguo de cuatro arreglos dobles[4], equivalente a un doble[16], no a un (doble *)[4].

No hay conversiones a los tipos de matriz, aunque hay conversiones a referencias o punteros a matrices que arrojan un valor a un doble [4] [4] intentarían construir una en la pila, equivalente a std :: string (parser.getMatrix (( )) - excepto que la matriz no suministra un constructor adecuado.Probablemente no quisieras hacer eso, incluso si pudieras.

Dado que el tipo codifica la zancada, necesita un tipo completo (doble[][] no sirve).Puede reinterpretar lanzar el void* a ((double[4][4])*), y luego tomar la referencia.Pero es más fácil escribir la matriz y devolver una referencia del tipo correcto en primer lugar:

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

Para profundizar en la respuesta seleccionada, observe esta línea.

const matrix& getMatrix() const

Esto es genial, no tienes que preocuparte por los punteros ni el casting.Estás devolviendo un referencia al objeto de matriz subyacente.Las referencias en mi humilde opinión son una de las mejores características de C++, que extraño cuando codifico en C puro.

Si no está familiarizado con la diferencia entre referencias y punteros en C++, Lee esto

En cualquier caso, hay que tener en cuenta que si el Parser El objeto que realmente posee el objeto de matriz subyacente queda fuera de alcance, cualquier código que intente acceder a la matriz a través de esa referencia ahora hará referencia a un objeto fuera de alcance y fallará.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top