Domanda

Dato il seguente codice:

class foo;

foo* instance = NULL;

class foo
{
public:
   explicit foo(int j)
    : i(j)
   {
      instance = this;
   }

   void inc()
   {
      ++i;
   }

private:
   int i;
};

Quanto segue utilizza un comportamento definito?

const foo f(0);

int main()
{
   instance->inc();
}

Lo chiedo perché sto utilizzando un registro di classe e poiché non lo modifico direttamente f sarebbe bello realizzarlo const, ma poi più tardi f viene modificato indirettamente dal registro.

MODIFICARE:Per comportamento definito intendo:L'oggetto è inserito in una posizione di memoria speciale su cui è possibile scrivere solo una volta?La memoria di sola lettura è fuori questione, almeno fino al constexpr di C++1x.I tipi primitivi costanti, ad esempio, vengono (spesso) inseriti nella memoria di sola lettura e facendo a const_cast su di esso può provocare un comportamento indefinito, ad esempio:

int main()
{
    const int i = 42;
    const_cast<int&>(i) = 0; // UB
}
È stato utile?

Soluzione

Si, è un comportamento indefinito, come da 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.

Si noti la vita di quell'oggetto inizia quando la chiamata al costruttore ha completato (3,8 / 1).

Altri suggerimenti

Se definisci un'istanza const dell'oggetto, poi scarti la const-ness e modifichi il contenuto dell'oggetto, otterrai un comportamento indefinito.

Dal suono delle cose, quello che vuoi è esattamente l'opposto:creare un'istanza non const dell'oggetto, quindi restituire un puntatore const a quell'oggetto alla maggior parte dei client, mentre il "proprietario" mantiene un puntatore non const all'oggetto in modo che possa modificare i membri come ritiene opportuno.

In genere gestiresti una situazione come questa definendo la classe con un ctor privato, quindi la maggior parte dei client non può creare oggetti di quel tipo.La classe dichiarerà quindi la classe proprietaria come amica, in modo che possa utilizzare il ctor privato e/o una funzione membro statica per creare istanze (o spesso solo un'istanza) dell'oggetto.La classe proprietaria quindi distribuisce puntatori (o riferimenti) agli oggetti const affinché i client possano utilizzarli.Non è necessario né un membro mutabile né eliminare la constness, perché il proprietario, che ha il "diritto" di modificare l'oggetto, ha sempre un puntatore non const (o, ancora, un riferimento) all'oggetto.I suoi client ricevono solo puntatori/riferimenti const, impedendo la modifica.

Questo può essere uno dei rari casi in cui potrebbe essere utilizzato la parola mutable poco conosciuto:

mutable int i;

i può ora essere modificato anche se l'oggetto è const. E 'utilizzato quando logicamente l'oggetto non cambia, ma in realtà lo fa.


Ad esempio:

class SomeClass
{
// ....
    void DoSomething() { mMutex.lock(); ...; }
    mutable Mutex mMutex;
}

Nel DoSomething() l'oggetto non logicamente cambiare eppure mMutex deve cambiare in modo da bloccarlo. Quindi ha senso per renderlo mutable, altrimenti alcuna istanza di SomeClass potrebbe essere const (supponendo che si blocca la muetx per ogni operazione).

Parlando un non-const (mediante dichiarazione) funzione membro const su un oggetto non è illegale di per sé. È possibile utilizzare qualsiasi metodo che si desidera per aggirare le restrizioni del compilatore:. O un const_cast esplicita o un trucco con il costruttore, come nel tuo esempio

Tuttavia, il comportamento è definito solo finché la funzione membro si sta chiamando non fare un tentativo di realtà fisicamente Modifica l'oggetto (ossia modificare membro non mutabile dell'oggetto costante) . Una volta che fa un tentativo di eseguire una modifica, il comportamento diventa indefinito. Nel tuo caso, il metodo inc modifica l'oggetto, il che significa che nel tuo esempio il comportamento è indefinito.

Basta chiamare il metodo, ancora una volta, è perfettamente legale.

E 'difficile dire l'intento con questi nomi arbitrari. Se i è inteso come un semplice contatore uso, e non è realmente considerato parte dei dati, allora è perfettamente opportuno dichiarare come mutable int i; Poi il const-ità di un'istanza non viene violata quando i viene modificato. D'altra parte, se è i dati significativi nello spazio da modellare, allora sarebbe una pessima cosa da fare.

A parte questo, però, il vostro esempio è un po 'di confusione per quello che ti sembra di essere chiedendo. foo* instance = NULL; è efficace (se confusamente) utilizzando un NULL come uno zero numerico e instance inizializzazione, che non è const; allora si inizializza separatamente f, che è const, ma mai riferimento a esso.

In GCC, almeno, il vostro costruttore dovrebbe essere explicit foo(int j) con la parola int.

Tuttavia, è perfettamente bene avere due puntatori allo stesso valore, uno const e l'altro no.

Perché non si fanno uso del cast const?

Qual è il motivo per rendere oggetto come const eventhough il suo stato non è costante?

make Inoltre seguente modifica:

explicit foo(int j = 0)    : i(j)   

{    instance = this;   }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top