2008-09-10 19 views
15

tengo una clase para analizar una matriz que mantiene el resultado en un miembro de la matriz:¿Cómo lidiar con matrices (declaradas en la pila) en C++?

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

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

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

la única manera que he llegado con la persona que llama pasar el resultado matriz a la función está haciendo el miembro público:

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

¿Es esta la única manera de hacer las cosas? Estoy asombrado de cómo las matrices multidimensionales inflexibles declaradas así son. Pensé que matrix_ sería esencialmente lo mismo que double** y que podría lanzar (con seguridad) entre los dos. Resulta que ni siquiera puedo encontrar insegura forma de lanzar entre las cosas. Digamos que añadir un descriptor de acceso a la clase Parser:

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

Esto compilará, pero no puedo usarlo, porque no parece ser una forma de emitir de nuevo al tipo de matriz raro:

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

error: 'tipo fundido': no ​​se puede convertir de 'void *' a 'const doble [4] [4]'

... con una adición intrigante:

No hay conversiones a tipos de matriz, aunque hay conversiones a referencias o punteros a las matrices de

no puedo determinar cómo lanzar a una referencia o puntero a la matriz tampoco, aunque probablemente no me ayude aquí.

Sin duda, en este punto el asunto es puramente académico, ya que los modelos void* son apenas más limpios que un solo miembro de la clase que se deja en libertad.

Respuesta

16

Aquí es una manera agradable, limpio:

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

    // ... 

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

    // ... 

private: 
    matrix matrix_; 
}; 

Ahora se trabaja con un nombre de tipo descriptivo en lugar de una matriz, pero ya que es un typedef el compilador todavía permite pasar a la función API inmutable eso toma el tipo base.

+0

Dale un golpe. :-) – Andrew

+0

porque usé un nombre de tipo más corto. Piensa en todo el tiempo que desperdiciaste escribiendo esos cinco caracteres extra ... ;-p – Shog9

+0

Y expandió el "// ..." s también ... Sin embargo, creo que debería aceptar el primero. Lo siento Andrew! – Owen

4

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

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

Luego pasa un puntero a su setter y copiar los datos en la matriz en su clase.

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

6

Pruebe 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; 
} 
4

pensé matrix_ sería esencialmente el mismo como un doble **

En C hay verdaderos matrices multidimensionales, no matrices de punteros a matrices, por lo que un doble [4] [4] es una matriz contigua de cuatro matrices dobles [4], equivalentes a un doble [16], no a (doble *) [4].

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

Dado que el tipo codifica la zancada, necesita un tipo completo (el doble [] [] no funcionará). Puede reinterpretar emitir el vacío * a ((doble [4] [4]) *), y luego tomar la referencia. Pero es más fácil de typedef la matriz y devolver una referencia del tipo correcto en el 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(); 
} 
2

dar más detalles sobre la respuesta seleccionada, observar esta línea

const matrix& getMatrix() const 

Esto es muy bueno, que Don No tienes que preocuparte por los punteros y el casting. Está devolviendo una referencia al objeto matriz subyacente. En mi humilde opinión referencias son una de las mejores características de C++, lo que echo de menos cuando se codifica en C. recta

Si no está familiarizado con la diferencia entre las referencias y punteros en C++, read this

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

Cuestiones relacionadas