Se la modifica di un oggetto const è un comportamento indefinito allora come costruttori e distruttori operano con accesso in scrittura?

StackOverflow https://stackoverflow.com/questions/2271046

Domanda

C ++ standard dice che modifica di un oggetto const originariamente dichiarato è un comportamento indefinito. Ma allora come fanno costruttori e distruttori operano?

class Class {
public:
    Class() { Change(); }
    ~Class() { Change(); }
    void Change() { data = 0; }
private:
    int data;
};

//later:
const Class object;
//object.Change(); - won't compile
const_cast<Class&>( object ).Change();// compiles, but it's undefined behavior

Voglio dire qui il costruttore e distruttore fanno esattamente la stessa cosa come il codice chiamante, ma essi sono autorizzati a modificare l'oggetto e il chiamante non è permesso -. Si imbatte in un comportamento indefinito

Come si suppone di lavorare sotto un'implementazione e secondo lo standard?

È stato utile?

Soluzione

Lo standard consente esplicitamente costruttori e distruttori a che fare con oggetti const. da 12,1 / 4 "Costruttori":

  

Un costruttore può essere richiamato per un const, volatile o oggetto const volatile. ... const e volatile semantica (7.1.5.1) non sono applicate su un oggetto in costruzione. Tale semantica diventano attive solo dopo la costruzione per l'oggetto più derivato (1.8) termina.

E 12.4 / 2 "Distruttori":

  

Un distruttore può essere richiamato per un const, volatile o oggetto const volatile. ... const e volatile semantica (7.1.5.1) non sono applicati su un oggetto sotto distruzione. Tale semantica smettono di essere in vigore una volta che il distruttore per l'oggetto più derivato (1.8) inizia.

Come sfondo, Stroustrup dice in "Progettazione ed evoluzione del C ++" (13.3.2 Affinamento del Defintion di const):

  

Per assicurare che alcuni, ma non tutti, gli oggetti const potrebbero essere immessi memoria di sola lettura (ROM), ho adottato la regola che qualsiasi oggetto che ha un costruttore (cioè inizializzazione runtime richiesta) non può essere posto in ROM, ma altri oggetti const possibile.

     

...

     

Un const dichiarata oggetto è considerato immutabile dal completamento del costruttore fino all'inizio del suo distruttore. Il risultato di una scrittura all'oggetto tra questi punti sono ritenute indefinito.

     

Quando originariamente progettazione const, mi ricordo sostenendo che il const ideale sarebbe un oggetto che è scrivibile fino a quando il costruttore era corsa, poi diventa di sola lettura da qualche magia di hardware, e, infine, con l'entrata in distruttore ridiventa scrivibile. Si potrebbe immaginare un'architettura etichettato che in realtà ha funzionato in questo modo. Tale implementazione potrebbe causare un errore di run-time se qualcuno potesse scrivere in un const oggetto definito. D'altra parte, qualcuno potrebbe scrivere a un const oggetto non definito che era stato passato come riferimento const o un puntatore. In entrambi i casi, l'utente avrebbe dovuto gettare via const prima. L'implicazione di questa visione è che gettando via const per un oggetto che è stato originariamente definito const e poi la scrittura è nella migliore delle ipotesi non definita, mentre facendo lo stesso ad un oggetto che non è stato originariamente definito const è legale e ben definito.

     

Si noti che con questa raffinatezza delle regole, il significato di const non dipende dal fatto che un tipo ha un costruttore o no; in linea di principio, lo fanno tutti. Qualsiasi oggetto const dichiarato ora può essere messo in ROM, essere collocato in segmenti di codice, protetto dal controllo di accesso, ecc, per garantire che non muta che riceve il suo valore iniziale. Tale protezione non è necessario, però, perché i sistemi attuali non possono in generale proteggere ogni const da ogni forma di corruzione.

Altri suggerimenti

Per approfondire quello che Jerry Coffin ha detto:. Lo standard rende l'accesso un oggetto const non definito, solo se si verifica che l'accesso durante la vita di un oggetto

7.1.5.1/4:

  

Se non fosse che ogni membro della classe dichiarata mutabile (7.1.1) può essere modificato, qualsiasi tentativo di modificare un oggetto const durante la sua vita (3,8) si traduce in un comportamento indefinito.

Il ciclo di vita dell'oggetto inizia solo dopo che il costruttore ha terminato.

3.8 / 1:

  

La durata di un oggetto di tipo T inizia quando:

     
      
  • stoccaggio con il corretto allineamento e la dimensione per il tipo T viene ottenuta, e
  •   
  • se T è un tipo di classe con un costruttore non banale (12.1), la chiamata al costruttore ha completato.
  •   

La norma in realtà non dice molto su come l'implementazione rende il lavoro, ma l'idea di base è piuttosto semplice: il const applica all'oggetto , non (necessariamente) per la memoria in cui è memorizzato l'oggetto. Dal momento che il ctor è parte di ciò che crea l'oggetto, non è davvero un oggetto fino a quando (a volte poco dopo) i rendimenti ctor. Allo stesso modo, dal momento che la dtor partecipa a distruggere l'oggetto, non è più in realtà che operano su un oggetto completo sia.

Ecco un modo che ignorare lo standard potrebbe portare a comportamenti scorretti. Si consideri una situazione come questa:

class Value
{
    int value;

public: 
    value(int initial_value = 0)
        : value(initial_value)
    {
    }

    void set(int new_value)
    {
        value = new_value;
    }

    int get() const
    {
        return value;
    }
}

void cheat(const Value &v);

int doit()
{
    const Value v(5);

    cheat(v);

    return v.get();
}

Se ottimizzata, il compilatore sa che v è const così potrebbe sostituire la chiamata a v.get() con 5.

Ma diciamo che in un'unità di traduzione diverso, avete definito cheat() in questo modo:

void cheat(const Value &cv)
{
     Value &v = const_cast<Value &>(cv);
     v.set(v.get() + 2);
}

Così, mentre sulla maggior parte delle piattaforme di questo verrà eseguito, il comportamento potrebbe cambiare a seconda di cosa l'ottimizzatore fa.

constness per un tipo definito dall'utente è diverso constness per un tipo incorporato. Constness quando utilizzato con tipi definiti dall'utente è detto di essere "constness logico". Il compilatore impone che solo funzioni membro dichiarate "const" può essere chiamato su un oggetto const (o puntatore, o di riferimento). Il compilatore non può allocare l'oggetto in una certa zona memoria a sola lettura, perché non-const funzioni utente deve essere in grado di modificare lo stato dell'oggetto (e anche const funzioni membro deve essere in grado di mutable quando viene dichiarata una variabile membro).

Per tipi built-in, credo che il compilatore è permesso di allocare l'oggetto in memoria di sola lettura (se la piattaforma supporta una cosa del genere). Così gettar via il const e modificando la variabile potrebbe causare un errore di protezione della memoria in fase di esecuzione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top