c++ class reference const

C++, Classes, Const y sintaxis extraña



reference (8)

Aunque getccp() es un método const , no hace ninguna promesa de lo que haces con la referencia que devuelve. El método en sí no modifica el objeto y, por lo tanto, no rompe las reglas.

Si devolviera una const ConstCheater& eso sería diferente.

Como muestra su ejemplo, hay mucha más complejidad para const que simplemente aplicarla a un objeto. C ++ FAQ tiene una sección sobre const correctness y, en particular , cubre el caso que está resaltando aquí.

Estaba volviendo a leer C ++ primer (4th ed.) Hoy - la sección sobre funciones de miembros y referencias de referencias, etc., y se me ocurrió este pequeño y extraño programa:

using std::cout; using std::endl; class ConstCheater { public: ConstCheater(int avalue) : ccp(this), value(avalue) {} ConstCheater& getccp() const {return *ccp;} int value; private: ConstCheater* ccp; }; int main() { const ConstCheater cc(7); //Initialize the value to 7 cout << cc.value << endl; cc.getccp().value = 4; //Now setting it to 4, even though it''s const! cout << cc.value << endl; cc.value = 4; //This is illegal return 0; }

Mi pregunta es: ¿por qué c ++ permite una sintaxis como esta? ¿Por qué puedo editar miembros de datos normales en una clase cuando se declara const? ¿No es el PUNTO de const hacer que no se puedan modificar los valores?


El constructor puede modificar el valor de un objeto const , sí. Pero si no fuera así, ¿qué podría hacer?

Como el constructor tiene dicho acceso, puede "reenviarlo" a otra persona o "guardarlo" para más adelante. Por supuesto, hacerlo podría ser una mala idea.

Esta es una instancia donde los mecanismos de seguridad de C ++ no evitan que desarrolles un programa mal formado. C ++ es cualquier cosa menos infalible. Entonces, ¡solo ten cuidado!


El problema real no es el comportamiento de ConstCheater::getccp() : es que no hay ningún error en la línea:

const ConstCheater cc(7);

que inicializa un puntero no const con lo que debería ser un const this puntero. Sin embargo, los constructores no pueden ser const (9.3.2 / 5, pero un poco de pensamiento debería hacer que sea obvio por qué). Entonces el constructor puede inicializar un puntero no const con un puntero a un objeto const (o un objeto que está a punto de convertirse en const). Ese es el agujero que estás manejando.

En cuanto a por qué está permitido, me imagino que sería difícil para el estándar tratar de cerrar el agujero, ya que tendría que enumerar todas las formas en que debería tratarse la const y todas las formas en que debería tratarse. non-const al construir un objeto const. Esa parece una tarea bastante difícil.


Lo que estás haciendo const es la referencia a un ConstCheater . Nada en ConstCheater está convirtiendo el value una const .


Los objetos no se vuelven const hasta que sus constructores terminan de hacer lo suyo. Así que this es un puntero a la memoria no constante cuando la almacena, pero cambia poco después. Es por eso que permitió la tarea en primer lugar, porque no hiciste nada mal. Eso significa que cpp es un puntero a const, pero el compilador no se da cuenta de eso. No tiene manera de; lo declaraste no const después de todo. Este sigue siendo un comportamiento indefinido, simplemente no es del tipo que tu compilador realmente puede esperar ayudarte a atrapar.


Su objeto no es completamente inmutable: es solo la referencia que creó para señalarlo que es constante.


Yo diría que la respuesta de Tony que marcó como correcta es incorrecta, y la respuesta de Michael Burr es correcta.

Para poner lo que dijo con más claridad (al menos para mí):

Hay dos lugares potenciales para el malentendido:

  1. La forma en que implícitamente funciona
  2. La forma en que this se interpreta durante la construcción de un objeto const

1. Const implícito

Const impst (la constricción interna de ConstCheater cuando se hace const ) no convierte a cc en un pointer-to-const sino más bien en un const-pointer , es decir cuando haces esto:

const ConstCheater cc(7);

Internamente va desde:

ConstCheater * ccp;

...a...

ConstCheater * const ccp;

... en lugar de ...

const ConstCheater * ccp;

eso puede haber sido esperado.

2. Construcción del objeto const

Lo extraño es que this se puede pasar al inicializador de cpp en el constructor ya que this , uno pensaría, debería tratarse como un pointer-to-const , y por lo tanto no es un valor válido para pasar a un const-pointer

Es decir, uno podría esperar:

...: ccp(this) ... // expected to fail but doesnt

fallar porque conceptualmente se puede esperar que esto sea (algo) equivalente a:

const ConstCheater cc(7); const ConstCheater * const this = &cc; // const-pointer-to-const

y así pensarías que:

ConstCheater * const ccp = this; //expected error!

¡fallaría! Pero no lo hace porque aparentemente durante la construcción aparentemente this se trata especialmente como si fuera:

const ConstCheater * this = &cc;

y así el objeto efectivamente no es const durante la construcción.

No estoy seguro de entender por completo el razonamiento, pero Michael Burr señala que parece haber una barrera lógica y técnica para proporcionar el comportamiento esperado, por lo que el estándar parece anular el comportamiento actual algo extraño.

Recientemente, hice una pregunta relacionada que fue: ¿Por qué C ++ no tiene un constructor const? pero hasta ahora no han entendido completamente el razonamiento de por qué sería insostenible, aunque supongo que supondría una carga para los desarrolladores de C ++ tener que definir un constructor de const torpe para cualquier clase con la que quisieran crear el objeto const.


const Qualifier restringe para llamar a los métodos no const en su objeto, por lo que el problema es su diseño que le permite dar una referencia no constante a un miembro mediante un método const. El enfoque común es

Member& getccp() {return *member;} const Member& getccp() const {return *member;}

En algunos casos, cuando la consistencia lógica de su objeto no sufre modificación externa de su miembro, puede permitir

Member& getccp() const {return *member;}

Otro ejemplo de diferencia en constness lógica y formal es los miembros mutable . Es decir, mutable puede ser un término que se calculó en la última invocación del método const , si obtiene la misma entrada en la próxima invocación, puede devolver fácilmente el valor almacenado.