2010-09-03 46 views
15

Estaba buscando a través de la fuente de OpenDE y me encontré con algún uso de sintaxis extraña del operador de indexación de matriz '[]' en una clase. Aquí está un ejemplo simplificado para mostrar la sintaxis:¿Qué significa esta sintaxis de C++ y por qué funciona?

#include <iostream> 

class Point 
{ 
public: 
    Point() : x(2.8), y(4.2), z(9.5) {} 

    operator const float *() const 
    { 
     return &x; 
    } 

private: 
    float x, y, z; 
}; 

int main() 
{ 
    Point p; 
    std::cout << "x: " << p[0] << '\n' 
       << "y: " << p[1] << '\n' 
       << "z: " << p[2]; 
} 

Salida:

x: 2.8 
y: 4.2 
z: 9.5 

¿Qué está pasando aquí? ¿Por qué funciona esta sintaxis? La clase Point no contiene operator [] sobrecargado y aquí este código intenta hacer una conversión automática para flotar en alguna parte.

Nunca antes había visto este tipo de uso: definitivamente parece inusual y sorprendente por decir lo menos.

Gracias

+4

Tenga en cuenta que para saber si está usando 'operator []' o una función de conversión de puntero, puede hacer esta extraña prueba: si '0 [p]' funciona, está usando una conversión de puntero. Si no funciona, pero si 'p [0]' funciona, está usando 'operator []'. –

Respuesta

17

p está siendo convierten implícitamente en un const float* const, que apunta a x. Por lo tanto, *p es x, *(p+1) es y, y así sucesivamente. Es una idea bastante extraña (¡y confusa!) Hacerlo de esta manera, por supuesto. Por lo general, es preferible almacenar x, y y z en una matriz y tener una función para obtener la matriz completa si realmente quieren hacer las cosas de esta manera.

+19

Sin mencionar que esto es completamente inseguro ya que la ubicación en la memoria de 'x',' y', y 'z' no está bien definida. –

+0

ahhh okie gracias. Ese es un código inteligente allí ... quizás demasiado inteligente: o – greatwolf

+2

@ D.Shawley: Un buen punto. Si bien un compilador "razonable" probablemente haría esto, no creo que haya ninguna garantía de que este diseño sea estándar. – rlbond

1

Esta es solo una forma de tratar los datos de sus miembros como una matriz. También puedes hacer esto con structs. Esto es útil cuando desea legibilidad, pero desea poder iterar sobre estructuras de datos simples. Un ejemplo de uso sería declarar una matriz de esta manera:

typedef struct { 
    CGFloat m11,m12,m13,m14; 
    CGFloat m21,m22,m23,m24; 
    CGFloat m31,m32,m33,m34; 
    CGFloat m41,m42,m43,m44; 
} CATransform3D; 

Es práctica para hacer referencia a cada celda por su nombre, sin embargo, también puede pasar alrededor de un puntero a M11 en todas partes (y C verá su estructura como una matriz, m11 es el primer elemento) e iterar sobre todos los elementos.

8

La idea aquí es dar acceso a los miembros del Punto por subíndice o nombre. Si quieres hacer eso, sin embargo, que sería mejor de sobrecarga operator[] algo como esto:

struct Point { 
    float x, y, z; 

    float &operator[](size_t subscript) { 
     switch(subscript) { 
      case 0: return x; 
      case 1: return y; 
      case 2: return z; 
      default: throw std::range_error("bad subscript"); 
     } 
    } 
}; 

De esta manera, si el compilador inserta relleno entre los flotadores, seguirá siendo el trabajo - y cualquiera que pueda leer C++ debería ser capaz de entenderlo sin ningún problema.

+1

Buen punto. Eso es lo que estoy pensando también. Sé que cuando estaba leyendo la fuente, no era obvio qué estaba haciendo el código. Creo que esto definitivamente cae bajo 'código que es demasiado inteligente para su propio bien'. : P – greatwolf

+0

+1 para el código "correcto" - seguro, maneja errores, es legible, etc. – JBRWilkinson