2009-12-18 22 views
16

en C++ un objeto pila-asignado puede ser declarado const:¿Puede un objeto asignado en el montón const en C++?

const Class object; 

después de que tratando de llamar a un método no const en tal objeto es un comportamiento indefinido:

const_cast<Class*>(&object)->NonConstMethod(); //UB 

Puede un heap-asignados objeto sea const con las mismas consecuencias? Quiero decir, ¿es posible que lo siguiente:

const Class* object = new Class(); 
const_cast<Class*>(object)->NonConstMethod(); // can this be UB? 

también es comportamiento indefinido?

+0

Hmm, después de publicar mi respuesta me di cuenta de que podría aplicarse también a su objeto asignado a la pila. ¿Puedes proporcionar más información sobre por qué el ejemplo de pila es UB? –

+0

El ejemplo de la pila es simplemente el más obvio. Por ejemplo, llamas a alguna función y pasas un puntero const a tal objeto y en algún lugar muy profundo de la pila de llamadas se hace un const_cast y se invoca un método no constreñido: bienvenido el UB, muy malo para la portabilidad. – sharptooth

+0

@sharptooth ¿Este párrafo 3.10/15 está en acción? –

Respuesta

16

Sí. Es legal construir y destruir un objeto de montón const. Al igual que con otros objetos const, los resultados de manipularlo como un objeto no const (por ejemplo, a través de const_cast de un puntero o referencia) producen un comportamiento indefinido.

struct C 
{ 
     C(); 
     ~C(); 
}; 

int main() 
{ 
     const C* const p = new const C; 

     C* const q = const_cast<C*>(p); // OK, but writes through q cause UB 

     // ... 

     delete p; // valid, it doesn't matter that p and *p are const 

     return 0; 
} 
+1

Ok, me siento un poco estúpido aquí, pero ¿cuál es el propósito de const_cast si los métodos de invocación en el puntero resultante siempre pueden ser UB? –

+0

Porque el comportamiento indefinido se puede definir en su plataforma de destino. const_cast sacrifica la portabilidad, que a menudo es una compensación aceptable. –

+0

@Pontus Todavía no lo entiendo; ¿El uso de 'const_cast' siempre causa UB? (según el estándar, no me importa si se define el resultado en alguna plataforma específica). –

10

En su ejemplo de pila, new devuelve un puntero a no const. El hecho de que lo hayas almacenado en un puntero para const (y luego const_cast lo devolvió a un puntero para no const) no cambia el hecho de que el objeto en sí no es const del mismo modo que el stack asignado uno es.

Sin embargo, puede crear un objeto constante en el montón:

const Class* object = new const Class(); 

En tal caso, echando a un puntero a no constante y llamar a un método no constante sería la misma situación como el objeto const stack-assigned.

(La idea de crear un objeto constante en el montón era nuevo para mí, que nunca había visto antes. Gracias a Charles Bailey.)

+5

Puede _can_ eliminar mediante un puntero const y un puntero a const. –

+1

'new' devuelve un puntero que es un valor r. Se puede asignar a un puntero const o non-const. El resultado de new puede ser un puntero a const o un puntero al objeto no const dependiendo del tipo de objeto 'newed'. En el caso de un objeto no const, el puntero puede asignarse a un puntero a non-const o a un puntero a reglas const (usual), pero si 'nuevo' es un objeto const, solo se puede asignar a un puntero a const. –

+0

Gracias por eso, he aclarado mi respuesta. –

1

Obviamente:

struct Foo { 
    const int Bar; 
    Foo() : Bar(42) { } 
}; 

Foo* foo = new Foo; 
const_cast<int&>(foo->Bar); // don't do this. 
+0

Interpretación inteligente de la pregunta, aunque un error leve en el código, debe ser 'int &' no 'int *'. – Aidiakapi

2

Sí, un montón de objetos asignados puede ser const. Consideremos este extracto del ejemplo en 7.1.5.1/5:

const int* ciq = new const int (3); // initialized as required 
int* iq = const_cast<int*>(ciq);  // cast required 
*iq = 4;        // undefined: modifies a const object 

El ejemplo que diste en la pregunta está bien porque usted no está pidiendo new para hacer un objeto constante; solo estás almacenando el resultado en un puntero-a-const.

0

const_cast puede causar UB cuando el objeto es realmente de solo lectura (por ejemplo, el compilador puede crear tales objetos cuando usa cadenas codificadas en su código, colocándolos en ciertas áreas de memoria que son de solo lectura) para algunos razón. Esto no sucederá con los objetos asignados en el montón, sin importar cómo conserve su referencia (indicador de const, referencia de referencia, lo que sea).

+0

La memoria de solo lectura no es el problema. El problema es si un objeto es const. El comportamiento indefinido * ocurrirá * con los objetos asignados en el montón si se les asigna const y usted los escribe a través de alguna ruta de acceso que no sea const (como la que obtiene de usar const_cast). Su implementación particular puede tomarse la libertad de definir qué sucede en esa situación, pero en lo que respecta al * estándar *, aún no está definido. –

1

No se olvide mutables miembros

No va a ser el comportamiento undefinied si el NonConstMethod sólo modifica mutable miembros calificados (ver 7.1.5.1 (4)) de una clase calificada const. Sí, de lo contrario es un comportamiento indefinido.

const A* p = new(const A); 
A *q = const_cast<A*>(p); 
q->NonConstMethodThatModifiesMembers();    // undefined behaviour! 
q->NonConstMethodThatOnlyModifiesMutableMembers(); // defined behaviour! 
+0

¿Podría explicar cómo llega a esa conclusión de la parte del estándar que citó? –

+0

OK, tienes razón. Supuse que un valor r siempre es no modificable, pero esa suposición no es válida. – WolfgangA

+0

Pareces haber convertido tu respuesta en una pregunta. –

Cuestiones relacionadas