2009-12-18 17 views
16

In C++ un oggetto stack allocato può essere dichiarato const:Un oggetto con allocazione heap può essere const in C++?

const Class object; 

dopo che il tentativo di chiamare un metodo non const su tale oggetto è un comportamento indefinito:

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

Può un mucchio allocate oggetto essere const con le stesse conseguenze? Voglio dire è possibile che il seguente:

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

è anche un comportamento non definito?

+0

Hmm, dopo aver inviato la mia risposta ho capito che poteva valere anche per l'oggetto assegnato allo stack. Puoi fornire ulteriori informazioni sul perché l'esempio di stack è UB? –

+0

L'esempio di stack è solo il più ovvio. Ad esempio, chiamate qualche funzione e passate un puntatore const a tale oggetto e da qualche parte in profondità nello stack di chiamate viene eseguito un const_cast e viene chiamato un metodo non-const: benvenuto all'UB, molto cattivo per la portabilità. – sharptooth

+0

@sharptooth Questo paragrafo 3.10/15 è in azione? –

risposta

16

Sì. È legale costruire e distruggere un oggetto heap const. Come con altri oggetti const, i risultati della manipolazione come oggetto non-const (ad esempio attraverso un const_cast di un puntatore o riferimento) causano un comportamento non definito.

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, mi sento un po 'stupido qui, ma qual è lo scopo di const_cast se invocare metodi sul puntatore risultante può sempre essere UB? –

+0

Poiché il comportamento non definito può essere definito sulla piattaforma di destinazione. const_cast sacrifica la portabilità - che spesso è un compromesso OK. –

+0

@Pontus Ancora non capisco; l'uso di 'const_cast' causa sempre UB? (secondo lo standard, non mi interessa se il risultato su qualche piattaforma specifica capita di essere definito). –

10

Nell'esempio di heap, new restituisce un puntatore a non-const. Il fatto di averlo memorizzato in un puntatore su const (e quindi su const_cast ed è tornato a un puntatore a non-const) non cambia il fatto che l'oggetto stesso non è const nello stesso modo dello stack allocato uno è.

Tuttavia, è possibile crea un oggetto const sul mucchio:

const Class* object = new const Class(); 

In tal caso, la fusione a un puntatore a non-const e chiamando un metodo non-const sarebbe la stessa situazione come oggetto const-allocato allo stack.

(L'idea di creare un oggetto const sul mucchio era nuovo per me, non avevo mai visto prima. Grazie a Charles Bailey.)

+5

È possibile _can_ eliminare tramite un puntatore const e un puntatore su const. –

+1

'new' restituisce un puntatore che è un valore rvalue. Può essere assegnato a un puntatore const o non const. Il risultato di new può essere un puntatore a const o un puntatore all'oggetto non-const in base al tipo di oggetto 'newed'. Nel caso di un oggetto non const, il puntatore può essere assegnato a un puntatore a non-const o a un puntatore a regole const (solite), ma se si 'nuovo' un oggetto const è possibile solo assegnare a un puntatore a const. –

+0

Grazie per quello, ho chiarito la mia risposta. –

1

Ovviamente:

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

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

Un'interpretazione intelligente della domanda, sebbene un leggero bug nel codice, dovrebbe essere 'int &' not 'int *'. – Aidiakapi

2

Sì, un oggetto mucchio allocato può essere const. Considerate questo estratto dall'esempio in 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 

L'esempio che ha dato per la questione va bene perché non stai chiedendo new per rendere un oggetto const; stai semplicemente memorizzando il risultato in un puntatore-a-const.

0

const_cast può causare UB quando l'oggetto è in realtà di sola lettura (ad esempio, il compilatore può creare tali oggetti quando si utilizzano stringhe codificate nel codice, posizionandoli in determinate aree di memoria che sono di sola lettura) per alcuni ragionare. Questo non succederà con gli oggetti allocati nell'heap, indipendentemente da come si mantiene il loro riferimento (puntatore const, riferimento const, qualunque).

+0

La memoria di sola lettura non è il problema. Il problema è se un oggetto è const. Comportamento indefinito * avverrà * con oggetti allocati nell'heap se sono allocati const e si scrive su di essi tramite un percorso di accesso non const (come quello che si ottiene usando const_cast). La tua particolare implementazione potrebbe prendere la libertà di definire ciò che accade in quella situazione, ma per quanto riguarda lo * standard *, è ancora indefinito. –

1

Non dimenticare mutevoli membri

Non sarà comportamento undefinied se il NonConstMethod modifica solo mutable membri qualificati (vedi 7.1.5.1 (4)) di una classe qualificato const. Sì, altrimenti è un comportamento indefinito.

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

Potresti spiegare come raggiungi questa conclusione dalla parte dello standard che hai citato? –

+0

OK, hai ragione. Supponevo che un valore di rvalore fosse sempre non modificabile, ma questa ipotesi non regge. – WolfgangA

+0

Sembra che tu abbia trasformato la tua risposta in una domanda. –

Problemi correlati