2009-11-29 16 views
9
class Foo { 
    protected: 
    QPoint& bar() const; 

    private: 
    QPoint m_bar; 
}; 

QPoint& Foo::bar() const { 
    return m_bar; 
} 

me dio este mensaje:¿Cómo devolver correctamente la referencia al miembro de la clase?

error: inicialización no válido de referencia del tipo 'QPoint &' de la expresión del tipo 'const QPoint'

Sin embargo funciona si la cambio a esto:

QPoint& Foo::bar() const { 
    return (QPoint&) m_bar; 
} 

1) No entiendo por qué el compilador dice que mi QPoint es const.

2) ¿Está bien dejar el molde allí?

Respuesta

21

En una función miembro no const de clase Foo el puntero this es del tipo Foo* const - es decir, el puntero es const, pero no la instancia que apunta. En una función de miembro constante, sin embargo, el puntero this es del tipo const Foo* const. Esto significa que el objeto al que apunta también es constante.

Por lo tanto, en su ejemplo, cuando se utiliza this->m_bar (de los cuales m_bar es simplemente la forma corta), entonces m_bar es un miembro de un objeto constante - por lo que no puede devolverlo como una referencia no const.

En realidad, esto tiene sentido desde un Punto de vista del diseño: Si eso Foo objeto es un objeto constante, y se le permite invocar Foo::bar de objetos constantes, entonces, si esto sería devolver una referencia no constante a algunos detalles internos las cuales se puede violín, sería capaz de cambiar el estado de un objeto constante.

Ahora tiene que mirar su diseño y preguntarse cómo llegó a este punto y cuál es su intención real. Si ese miembro m_bar no es realmente parte del estado del objeto (por ejemplo, solo está allí para fines de depuración), entonces podría considerar hacerlo mutable. Si es parte del estado del objeto, entonces debe preguntarse por qué desea devolver una referencia no constante a algunos datos internos de un objeto constante. Haga que el miembro funcione como no const, o devuelva una referencia constante, o sobrecargue la función de miembro:

class Foo { 
public: 
    const QPoint& bar() const {return m_bar;} 
     QPoint& bar()  {return m_bar;} 
    // ... 
}; 
+2

Gracias. Pensé que el calificador de función const estaba destinado a garantizar solo que la función en sí no modificará el objeto. Quiero que m_bar sea modificable, así que eliminé el calificador. – Dimitris

+0

En realidad, evita que la función modifique el objeto, pero lo hace solo al entregar un objeto const a la función. Sin embargo, que la función para la cual no puede distribuir una referencia no constante es más que un efecto secundario accidental: no debería poder obtener referencias no constantes a un objeto constante de ninguna manera. – sbi

3

La función en la función le dice al compilador que la función no modifica ninguna variable miembro. Sin embargo, como devuelve una referencia a la variable miembro, no puede garantizar eso más y, por lo tanto, tiene el error de compilación.

El compilador no espera que su variable miembro sea const pero el retorno de la función sea const. Retire la const de la función def.

4

Lo que estás tratando de hacer es un no-no. No desea devolver un QPoint & desde la función de su barra ya que esto rompe la encapsulación porque una persona que llama ahora puede modificar m_bar fuera de QPoint. (afortunadamente tienes la función declarada como const o no obtendrías un error aquí y aún estarías rompiendo la encapsulación).

lo que quiere en este caso es

const QPoint &Foo::bar() const 
{ 
    return m_bar; 
} 

Editar basado en comentario de usuario:

en mi humilde opinión, esto sería mejor resuelve añadiendo un setX a Foo lugar de llamar a un no-const función de una referencia no const devuelta por una función de acceso que realmente debería ser const. Sobrecargar la función de acceso para eliminar la const solo pone una curita sobre el problema real y simplemente oculta el hecho de que la encapsulación está comprometida. Al agregar setX a Foo corrige el problema de encapsulamiento y también tapona la interfaz filtrada que está creando al devolver una referencia no constante a los datos privados.

Por ejemplo, si se pone foo.bar().setX(100); en muchas partes de la aplicación y luego cambia el tipo de QPoint a uno que no implementa setX o simplemente cambiar el nombre de la función setX decir setPointX tiene problemas b/c que tiene ahora para cambiar el nombre/refactorizar todas estas llamadas. Crear un setX en Foo hace que el código sea más fácil de llamar al foo.setX(100) frente al foo.bar().setX(100), es const correcto y encapsula sus datos. Si cambió QPoint para que fuera solo 2 coordenadas xey en Foo, nada fuera de su clase tendría que cambiar b/c, tiene una buena encapsulación.

+0

Foo :: bar() es un descriptor de acceso. Quiero poder modificar QPoint como foo.bar(). SetX (100); Pensé que el calificador de función const estaba destinado a garantizar que la función en sí misma no modificaría el objeto. ¡Gracias de todos modos! – Dimitris

+0

Tengo una respuesta actualizada basada en sus comentarios. –

3

o bien utilizar

const QPoint& Foo::bar() const { 
    return m_bar; 
} 

o

QPoint& Foo::bar() { 
    return m_bar; 
} 

supongo que también podría declarar m_bar como:

mutable QPoint m_bar; 

EDIT: defensa mutable

@tstenner:
mutable tiene su lugar. Definitivamente no es 'malvado' más que vacío * o casting. Asumir algo como lo siguiente:

class foo { 
    SomeType bar; 

public: 
    foo() : bar() { } 

    const SomeType& bar() const { return a; } 
}; 

En este caso, la barra se construye siempre, incluso si la barra() no se llama. Esto puede estar bien, pero si SomeType tiene un constructor costoso, puede ser preferible permitir la instanciación lenta de la barra.

considerar:

class foo2 { 
    mutable SomeType* bar; 
    mutable bool cached; 

public: 
    foo2() : bar(0), cached(false) { } 

    const SomeType& bar() const { 
     if(! cached) { 
      cached = true; 
      bar = new SomeType(); 
     } 
     return *bar; 
    } 
}; 

Esto permite foo2 para imitar una clase de instancia no perezoso, mientras se crea una instancia con pereza. Por supuesto, la mutabilidad de foo2 tiene implicaciones para la seguridad del hilo, pero estos se pueden superar bloqueando si es necesario.

+2

No desea utilizar mutable, ya que es malo. – tstenner

+1

Mutable se debe usar con moderación, pero tiene usos que no son malos. Por ejemplo, si tiene una función const accessor/member que necesita leer un objeto al que acceden varios hilos y se sincroniza a través de un mutex de clase, necesitaría hacer el mutex mutable para que las funciones const puedan leer el objeto de forma segura. –

2

Si le permitió devolver una referencia a la variable, entonces podría modificar una instancia de const de la clase . Considere este código:

void baz(const Foo &foo) 
{ 
    QPoint &ref = foo.bar(); //if it was allowed... 
    ref = 5; // ...you could have modified a const object foo! 
} 

Por lo tanto, el compilador le impide hacer eso.

Debe declarar su método devolviendo const QPoint& o revisar su comprensión de lo que realmente es const.

Alguien puede aconsejarle que use mutable. No lo hagas mutable La palabra clave es permitir la implementación de la clase para modificar las variables de miembros internos de los objetos const (por ejemplo, para fines de memorización), en lugar de exponer la variable no const (!) Al código externo.

Cuestiones relacionadas