2010-06-01 5 views
6

Estaba convirtiendo una estructura en una clase para poder aplicar una interfaz setter para mis variables.
No quise cambiar todas las instancias donde se leyó la variable. Así que convierte esto:¿Algún problema con esta lengua de interfaz de acceso const referencia C++?

struct foo_t { 
    int x; 
    float y; 
}; 

a esto:

class foo_t { 
    int _x; 
    float _y; 
public: 
    foot_t() : x(_x), y(_y) { set(0, 0.0); } 

    const int &x; 
    const float &y; 

    set(int x, float y) { _x = x; _y = y; } 
}; 

estoy interesado en esto porque parece modelar idea de C# 's de las propiedades públicas de sólo lectura.
Compila muy bien, y todavía no he visto ningún problema.

Además de la repetición de las referencias de const en el constructor, ¿cuáles son las desventajas de este método?
¿Algún problema de alias extraño?
¿Por qué no he visto este idioma antes?

+0

¿Por qué es const int & x; const float & y; ¿necesario? – Arun

+0

@ArunShaha Él lo explica en su pregunta. El objetivo es proporcionar un "acceso" a 'x' y' y' que mantenga la misma sintaxis que el acceso a las variables en la estructura. –

+1

C# y C++ son idiomas completamente diferentes con sus propios estilos y usos. Copiar el estilo de un idioma en otro llevará a muchos problemas, es mucho más simple aprender las mejores técnicas para el nuevo idioma, no copiar estilos que pueden no ser compatibles (como el horrible concepto de geter/seter) –

Respuesta

5

Existe un problema de alias porque expone una referencia a los datos internos de foo_t, es posible que el código externo a un objeto foo_t retenga referencias en sus datos más allá de la duración del objeto. Considere:

foo_t* f = new foo_t(); 
const int& x2 = f->x; 
delete f; 
std::cout << x2; // Undefined behavior; x2 refers into a foo_t object that was deleted 

O, aún más simple:

const int& x2 = foo_t().x; 
std::cout << x2; // Undefined behvior; x2 refers into a foo_t object that no longer exists 

Estos no son ejemplos particularmente realistas, pero este es un problema potencial cuando un objeto expone o devuelve una referencia a sus datos (pública o privada) Por supuesto, es posible conservar una referencia al objeto foo_t más allá de su tiempo de vida, pero puede ser más difícil pasar por alto o hacerlo por accidente.

No es que este sea un argumento en contra de lo que estás haciendo. De hecho, he usado este patrón antes (por una razón diferente) y no creo que haya algo intrínsecamente incorrecto en él, aparte de la falta de encapsulación, que pareces reconocer. El problema anterior es algo de lo que hay que estar consciente.

+0

Estaba sospechando algo como esto - gracias. – mskfisher

0

Su enfoque no es flexible. Cuando tiene un getter/setter para cada variable, significa que no tiene que volver a escribir su método set si agrega algo a su clase.

No es bueno porque no se puede tener const y non-const captadores (que se utilizan raramente, pero a veces podría ser útil).

No puede copiar referencias, por lo tanto, su clase no se puede copiar.

Además, al haber inicializado referenciado en su clase de medios de memoria extra y si estamos hablando de, por ejemplo, la clase vértice (aunque creo que no debería ser una clase en realidad), esto podría convertirse en un desastre.


[Todo lo que sigue es completamente subjetivo]

Propósito de captadores y definidores, en mi opinión, no es la modificación sobre simple, sino más bien de la encapsulación de una secuencia de acciones que da lugar a la modificación de valor o devuelve el valor (que tratamos como resultado visible).

Personalmente, struct en el caso de su ejemplo sería más eficaz, ya que ajusta los datos POD y lógicamente "lo estructura".

11

Un problema es que su clase ya no se puede copiar ni asignar, por lo que no se puede almacenar en contenedores C++ como vectores. Otra es que los experimentados programadores de C++ que mantengan su código lo verán y exclamarán "WTF !!" muy fuerte, lo cual nunca es algo bueno.

+1

Touche. Pero podría arreglarlo definiéndolos (lo dejé para abreviar). – mskfisher

+0

(Me gusta cómo su edición hizo mi comentario ambiguo; ahora podría referirme a "definir constructores de copia" o "definir programadores experimentados de C++".) – mskfisher

+0

@mskfisher Tal es la maravilla de SO, el hecho de que usted haya comentado no debería evitar que mejore la respuesta. Cualquiera que esté confundido siempre puede mirar el historial de edición. Y debo decir que encontré su comentario ambiguo para empezar: ¿qué son "ellos" en el contexto de mi respuesta original? –

2

También podría hacer algo como esto, que trabaja para construida en tipos: (Lo siento si este fragmento de código contiene errores, pero se entiende la idea)

template <typename T, typename F> 
class read_only{ 
    typedef read_only<T, F> my_type; 
    friend F; 

public: 
    operator T() const {return mVal;} 

private: 
    my_type operator=(const T& val) {mVal = val; return *this;} 
    T mVal; 
}; 


class MyClass { 
public: 
    read_only <int, MyClass> mInt; 
    void MyFunc() { 
     mInt = 7; //Works 
    } 
}; 

AnyFunction(){ 
    MyClass myClass; 
    int x = myClass.mVal; // Works (okay it hasnt been initalized yet so you might get a warning =) 
    myClass.mVal = 7; // Error 
} 
+0

Eso es bastante inteligente, tengo que decir. –

+0

También me gusta que haya evitado la referencia constante para el retorno por valor, lo que elimina la queja de Tyler sobre mi método. – mskfisher

+1

El parámetro de la plantilla AFAIR no puede ser amigo, por lo que no funcionará e incluso si funcionó debería ser "clase de amigo F". Hay trucos para solucionarlos porque son desagradables y no funcionan en todas partes. – Tomek

Cuestiones relacionadas