Qual è la semantica di una funzione membro const?
Domanda
Capisco che alla funzione non è consentito modificare lo stato dell'oggetto, ma pensavo di aver letto da qualche parte che al compilatore era consentito presumere che se la funzione fosse stata chiamata con gli stessi argomenti, avrebbe restituito lo stesso valore e quindi avrebbe potuto riutilizzare un valore memorizzato nella cache, se disponibile.per esempio.
class object
{
int get_value(int n) const
{
...
}
...
object x;
int a = x.get_value(1);
...
int b = x.get_value(1);
quindi il compilatore potrebbe ottimizzare la seconda chiamata e utilizzare il valore in un registro o semplicemente farlo b = a;
È vero?
Soluzione
const
riguarda la semantica del programma e non i dettagli di implementazione.Dovresti contrassegnare una funzione membro const
quando non cambia lo stato visibile dell'oggetto e dovrebbe essere richiamabile su un oggetto che è esso stesso const
.All'interno di una const
funzione membro di una classe X
, il tipo di this
È X const *
:puntatore alla costante X
oggetto.Pertanto tutte le variabili membro sono effettivamente const
all'interno di quella funzione membro (eccetto mutable
quelli).Se hai un const
oggetto, puoi solo chiamare const
funzioni membro su di esso.
Puoi usare mutable
per indicare che una variabile membro può cambiare anche all'interno di a const
funzione membro.Questo viene in genere utilizzato per identificare le variabili utilizzate per la memorizzazione nella cache dei risultati o per variabili che non influenzano lo stato effettivamente osservabile come i mutex (è comunque necessario bloccare il mutex nel const
funzioni membro) o utilizzare i contatori.
class X
{
int data;
mutable boost::mutex m;
public:
void set_data(int i)
{
boost::lock_guard<boost::mutex> lk(m);
data=i;
}
int get_data() const // we want to be able to get the data on a const object
{
boost::lock_guard<boost::mutex> lk(m); // this requires m to be non-const
return data;
}
};
Se conservi i dati tramite puntatore anziché direttamente (inclusi puntatori intelligenti come std::auto_ptr
O boost::shared_ptr
) quindi il puntatore diventa const
in un const
funzione membro, ma non i dati puntati, quindi è possibile modificare i dati puntati.
Per quanto riguarda la memorizzazione nella cache:in generale il compilatore non può farlo perché lo stato potrebbe cambiare tra una chiamata e l'altra (specialmente nel mio esempio multi-thread con il mutex).Tuttavia, se la definizione è in linea, il compilatore può inserire il codice nella funzione chiamante e ottimizzare ciò che può vedere lì.Ciò potrebbe comportare la funzione effettivamente essere chiamato solo una volta.
La prossima versione del Standard C++ (C++0x) avrà una nuova parola chiave constexpr
.Funzioni contrassegnate constexpr
restituire un valore costante, in modo che i risultati possano essere memorizzati nella cache.Ci sono dei limiti su cosa puoi fare in tale funzione (in modo che il compilatore possa verificare questo fatto).
Altri suggerimenti
La parola chiave mutevole sulle variabili membro consente alle funzioni const di alterare lo stato dell'oggetto in questione.
E no, non memorizza nella cache i dati (almeno non tutte le chiamate) poiché il seguente codice è una funzione const valida che cambia nel tempo:
int something() const { return m_pSomeObject->NextValue(); }
Si noti che il puntatore può essere const, sebbene l'oggetto puntato non sia const, pertanto la chiamata a NextValue su SomeObject può alterare o meno il proprio stato interno.Ciò fa sì che la funzione qualcosa restituisca valori diversi ogni volta che viene chiamata.
Tuttavia, non posso rispondere a come funziona il compilatore con i metodi const.Ho sentito dire che può ottimizzare alcune cose, anche se dovrei cercarlo per esserne sicuro.
NO.
Un metodo const è un metodo che non modifica lo stato dell'oggetto (es.i suoi campi), ma non si può presumere che, dato lo stesso input, venga determinato il valore restituito di un metodo const.In altre parole, const
La parola chiave NON implica che la funzione sia uno a uno.Ad esempio, un metodo che restituisce l'ora corrente è un metodo const ma il suo valore restituito cambia tra una chiamata e l'altra.
La parola chiave const su una funzione membro contrassegna il file Questo parametro come costante.La funzione può comunque disattivare i dati globali (quindi non può essere memorizzata nella cache), ma non i dati degli oggetti (consentendo chiamate su oggetti const).
In questo contesto, a const
la funzione membro significa questo this
è trattato come a const
anche il puntatore.In termini pratici, significa che non è consentito modificare lo stato di this
all'interno di un const
funzione membro.
Per le funzioni senza effetti collaterali (ovvero ciò che stai cercando di ottenere), GCC ha un "attributo funzione" chiamato pure
(lo usi dicendo __attribute__((pure))
): http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
Ne dubito, la funzione potrebbe ancora essere definita una funzione globale che ha alterato lo stato del mondo e non violare const.
Oltre al fatto che la funzione membro può modificare i dati globali, è possibile che la funzione membro modifichi i membri mutabili esplicitamente dichiarati dell'oggetto in questione.
Corey ha ragione, ma tieni presente che tutte le variabili membro contrassegnate come mutevole Potere essere modificato nelle funzioni membro const.
Significa anche che queste funzioni possono essere chiamate da altre funzioni const o tramite altri riferimenti const.
Modificare:Accidenti, è stato battuto di 9 secondi....9!!!:)
I metodi const possono anche modificare le localizzazioni statiche.Ad esempio, quanto segue è perfettamente legale (e chiamate ripetute a bar() restituiranno valori crescenti, non uno 0 memorizzato nella cache):
class Foo
{
public:
int bar() const
{
static int x = 0;
return x++;
}
};