2010-02-03 12 views
19

Recientemente he encontrado algo de código como este:¿Qué es útil sobre un parámetro referencia-a-matriz?

typedef int TenInts[10]; 
void foo(TenInts &arr); 

¿Qué se puede hacer en el cuerpo de foo() que es útil, que no se podía hacer si la declaración fue:

void foo(int *arr); // or, 
void foo(int arr[]); // or, 
void foo(int arr[10]); // ? 

he encontrado una pregunta que pregunta how to pass a reference to an array. Supongo que estoy preguntando por qué.

Además, solo one answer para "¿Cuándo es útil el puntero al array?" discutí los parámetros de la función, así que no creo que esta sea una pregunta duplicada.

Respuesta

40

El parámetro reference-to-array no permite que el tipo de matriz decaiga a tipo de puntero. es decir, el tipo de matriz exacto se conserva dentro de la función. (Por ejemplo, puede usar el truco sizeof arr/sizeof *arr en el parámetro y obtener el recuento de elementos). El compilador también realizará la comprobación de tipos para asegurarse de que el tipo de argumento de la matriz sea exactamente el mismo que el tipo de parámetro de la matriz, es decir, si el parámetro se declara como una matriz de 10 entradas, se requiere que el argumento sea una matriz de exactamente 10 Ints y nada más.

De hecho, en situaciones en las que el tamaño de la matriz se fija en tiempo de compilación, utilizando una referencia-a-array (o puntero-a-array) declaraciones de parámetros se pueden preceived como el primario, forma preferida para pasar una matriz. La otra variante (cuando el tipo de matriz puede decaer a tipo de puntero) se reserva para situaciones en las que es necesario pasar matrices de en tiempo de ejecución.

Por ejemplo, la forma correcta de pasar una matriz de tamaño en tiempo de compilación para una función es

void foo(int (&arr)[10]); // reference to an array 

o

void foo(int (*arr)[10]); // pointer to an array 

Una manera posiblemente incorrecto sería el uso de un "decaído" aproximación

void foo(int arr[]); // pointer to an element 
// Bad practice!!! 

El enfoque "deteriorado" debe reservarse normalmente para matrices de tiempo de ejecución si ze y está acompañado normalmente por el tamaño real de la matriz en un parámetro independiente

void foo(int arr[], unsigned n); // pointer to an element 
// Passing a run-time sized array 

En otras palabras, no hay realmente ninguna matriz puntero a "por qué" cuando se trata de hacer referencia a array (o) pasando. Se supone que debes usar este método de forma natural, de forma predeterminada, siempre que puedas, si el tamaño de la matriz está fijado en tiempo de compilación. La pregunta "por qué" realmente debería surgir cuando utilizas el método "decaído" de pasar array. El método "decaído" solo se supone que se usa como un truco especializado para pasar arreglos de tamaño de tiempo de ejecución.

Lo anterior es básicamente una consecuencia directa de un principio más genérico.Cuando tiene un objeto "pesado" del tipo T, normalmente lo pasa por el puntero T * o por referencia T &. Las matrices no son una excepción de este principio general. No tienen ninguna razón para estarlo.

Tenga en cuenta que, en la práctica, a menudo tiene sentido escribir funciones que funcionan con matrices de tamaño de tiempo de ejecución, especialmente cuando se trata de funciones genéricas a nivel de biblioteca. Tales funciones son más versátiles. Eso significa que a menudo hay una buena razón para usar el enfoque "decaído" en el código de la vida real. Sin embargo, esto no excusa al autor del código de reconocer las situaciones cuando el tamaño de la matriz se conoce en tiempo de compilación y usando la referencia -metodo de macetas en consecuencia.

+0

Gracias, excelente respuesta. – Dan

+4

... aunque estoy en desacuerdo con su última edición. Una matriz es un objeto "pesado", pero no se puede pasar una matriz por valor, a menos que la coloques en una estructura o algo. – Dan

7

Una diferencia es que (se supone que) es imposible pasar una referencia nula. Entonces, en teoría, la función no necesita verificar si el parámetro es nulo, mientras que un parámetro int * arr podría pasarse nulo.

+0

Muy buen punto, que no se me había ocurrido. – Dan

+0

En realidad, este consejo tiene un capítulo completo dedicado a Meyer's "More effectice C++". Algo en las líneas de * "Si un parámetro puede ser nulo, páselo por un puntero, de lo contrario páselo por referencia" *. – Vorac

+1

@Vorac: Eso es un consejo obsoleto. Debemos evitar punteros crudos, y querer hacer un parámetro opcional no es razón suficiente para dejar de evitarlos. –

0

Puede asegurarse de que la función solo se invoca en las matrices int de tamaño 10. Esto puede ser útil desde el punto de vista de la verificación de tipos.

0

Obtiene un significado más semántico con respecto a lo que la función está esperando.

2

Puede escribir una plantilla de función para averiguar el tamaño de una matriz en tiempo de compilación.

template<class E, size_t size> 
size_t array_size(E(&)[size]) 
{ 
    return size; 
} 

int main() 
{ 
    int test[] = {2, 3, 5, 7, 11, 13, 17, 19}; 
    std::cout << array_size(test) << std::endl; // prints 8 
} 

No más sizeof(test)/sizeof(test[0]) para mí ;-)

+1

Las soluciones 'sizeof()' dan una constante de tiempo de compilación, su función de plantilla no (a menos que agregue 'constexpr' con C++ 0x). Ver p. aquí para encontrar una solución: http://stackoverflow.com/questions/1500363/compile-time-sizeof-array-without-using-a-macro/1500517#1500517 –

Cuestiones relacionadas