Qual è il significato di const
in dichiarazioni come queste? Il const
mi confonde.
class foobar
{
public:
operator int () const;
const char* foo() const;
};
Qual è il significato di const
in dichiarazioni come queste? Il const
mi confonde.
class foobar
{
public:
operator int () const;
const char* foo() const;
};
Risposte:
Quando aggiungi la const
parola chiave a un metodo, il this
puntatore diventa essenzialmente un puntatore const
all'oggetto e non puoi quindi modificare i dati dei membri. (A meno che non lo usi mutable
, ne parleremo più avanti).
La const
parola chiave fa parte della firma delle funzioni, il che significa che è possibile implementare due metodi simili, uno che viene chiamato quando l'oggetto è const
e uno che non lo è.
#include <iostream>
class MyClass
{
private:
int counter;
public:
void Foo()
{
std::cout << "Foo" << std::endl;
}
void Foo() const
{
std::cout << "Foo const" << std::endl;
}
};
int main()
{
MyClass cc;
const MyClass& ccc = cc;
cc.Foo();
ccc.Foo();
}
Questo produrrà
Foo
Foo const
Nel metodo non const è possibile modificare i membri dell'istanza, cosa che non è possibile eseguire nella const
versione. Se si modifica la dichiarazione del metodo nell'esempio sopra con il codice riportato di seguito, verranno visualizzati degli errori.
void Foo()
{
counter++; //this works
std::cout << "Foo" << std::endl;
}
void Foo() const
{
counter++; //this will not compile
std::cout << "Foo const" << std::endl;
}
Questo non è completamente vero, perché è possibile contrassegnare un membro come mutable
e un const
metodo può quindi modificarlo. È principalmente usato per contatori interni e cose. La soluzione sarebbe il seguente codice.
#include <iostream>
class MyClass
{
private:
mutable int counter;
public:
MyClass() : counter(0) {}
void Foo()
{
counter++;
std::cout << "Foo" << std::endl;
}
void Foo() const
{
counter++; // This works because counter is `mutable`
std::cout << "Foo const" << std::endl;
}
int GetInvocations() const
{
return counter;
}
};
int main(void)
{
MyClass cc;
const MyClass& ccc = cc;
cc.Foo();
ccc.Foo();
std::cout << "Foo has been invoked " << ccc.GetInvocations() << " times" << std::endl;
}
quale sarebbe uscita
Foo
Foo const
Foo has been invoked 2 times
La const significa che il metodo promette di non alterare alcun membro della classe. Saresti in grado di eseguire i membri dell'oggetto che sono così contrassegnati, anche se l'oggetto stesso fosse segnato const
:
const foobar fb;
fb.foo();
sarebbe legale.
Vedi quanti e quali sono gli usi di "const" in C ++? per maggiori informazioni.
Il const
qualificatore indica che i metodi possono essere chiamati su qualsiasi valore di foobar
. La differenza viene quando si considera di chiamare un metodo non const su un oggetto const. Valuta se il tuo foobar
tipo aveva la seguente dichiarazione del metodo extra:
class foobar {
...
const char* bar();
}
Il metodo bar()
non è const e vi si può accedere solo da valori non const.
void func1(const foobar& fb1, foobar& fb2) {
const char* v1 = fb1.bar(); // won't compile
const char* v2 = fb2.bar(); // works
}
L'idea alla base const
è però quella di contrassegnare metodi che non altereranno lo stato interno della classe. Questo è un concetto potente ma non è effettivamente applicabile in C ++. È più una promessa che una garanzia. E uno che è spesso rotto e facilmente rotto.
foobar& fbNonConst = const_cast<foobar&>(fb1);
const
è quella di segnare metodi che non altereranno lo stato interno della classe". Questo è davvero quello che stavo cercando.
const
?
Queste const indicano che il compilatore si guasta se il metodo "con const" modifica i dati interni.
class A
{
public:
A():member_()
{
}
int hashGetter() const
{
state_ = 1;
return member_;
}
int goodGetter() const
{
return member_;
}
int getter() const
{
//member_ = 2; // error
return member_;
}
int badGetter()
{
return member_;
}
private:
mutable int state_;
int member_;
};
Il test
int main()
{
const A a1;
a1.badGetter(); // doesn't work
a1.goodGetter(); // works
a1.hashGetter(); // works
A a2;
a2.badGetter(); // works
a2.goodGetter(); // works
a2.hashGetter(); // works
}
Leggi questo per maggiori informazioni
const
funzioni membro che non menziona mutevole è nella migliore delle ipotesi incompleta.
La risposta di Blair è nel segno.
Tuttavia, è presente un mutable
qualificatore che può essere aggiunto ai membri dei dati di una classe. Qualsiasi membro così contrassegnato può essere modificato in un const
metodo senza violare il const
contratto.
Potresti voler usare questo (per esempio) se vuoi che un oggetto ricordi quante volte viene chiamato un metodo particolare, senza influire sulla costanza "logica" di quel metodo.
Significato di una funzione membro Cost in C ++ Conoscenza comune: la programmazione intermedia essenziale fornisce una chiara spiegazione:
Il tipo di questo puntatore in una funzione membro non const di una classe X è X * const. Cioè, è un puntatore costante a una X non costante (vedere Const Pointers e Pointers to Const [7, 21]). Poiché l'oggetto a cui si riferisce questo non è const, può essere modificato. Il tipo di questo in una funzione membro const di una classe X è const X * const. Cioè, è un puntatore costante a una X costante. Poiché l'oggetto a cui si riferisce è const, non può essere modificato. Questa è la differenza tra le funzioni membro const e non const.
Quindi nel tuo codice:
class foobar
{
public:
operator int () const;
const char* foo() const;
};
Puoi pensarlo come questo:
class foobar
{
public:
operator int (const foobar * const this) const;
const char* foo(const foobar * const this) const;
};
this
non lo è const
. Il motivo per cui non può essere modificato è che si tratta di un valore.
quando usi const
nella firma del metodo (come hai detto const char* foo() const;
:) stai dicendo al compilatore che la memoria indicata da this
non può essere modificata con questo metodo (che è foo
qui).
Vorrei aggiungere il seguente punto.
Puoi anche renderlo un const &
econst &&
Così,
struct s{
void val1() const {
// *this is const here. Hence this function cannot modify any member of *this
}
void val2() const & {
// *this is const& here
}
void val3() const && {
// The object calling this function should be const rvalue only.
}
void val4() && {
// The object calling this function should be rvalue reference only.
}
};
int main(){
s a;
a.val1(); //okay
a.val2(); //okay
// a.val3() not okay, a is not rvalue will be okay if called like
std::move(a).val3(); // okay, move makes it a rvalue
}
Sentiti libero di migliorare la risposta. Non sono un esperto
*this
è sempre un lvalue, anche se la funzione membro è qualificata rvalue-ref e viene chiamata su un rvalue. Esempio .
La parola chiave const utilizzata con la dichiarazione della funzione specifica che si tratta di una funzione membro const e che non sarà in grado di modificare i membri dei dati dell'oggetto.
https://isocpp.org/wiki/faq/const-correctness#const-member-fns
Che cos'è una "
const
funzione membro"?Una funzione membro che controlla (piuttosto che muta) il suo oggetto.
Una
const
funzione membro è indicata da unconst
suffisso subito dopo l'elenco dei parametri della funzione membro. Le funzioni membro con unconst
suffisso sono chiamate "funzioni membro const" o "ispettori". Le funzioni membro senzaconst
suffisso sono chiamate "funzioni membro non const" o "mutatori".class Fred { public: void inspect() const; // This member promises NOT to change *this void mutate(); // This member function might change *this }; void userCode(Fred& changeable, const Fred& unchangeable) { changeable.inspect(); // Okay: doesn't change a changeable object changeable.mutate(); // Okay: changes a changeable object unchangeable.inspect(); // Okay: doesn't change an unchangeable object unchangeable.mutate(); // ERROR: attempt to change unchangeable object }
Il tentativo di chiamare
unchangeable.mutate()
è un errore rilevato al momento della compilazione. Non esiste spazio di runtime o penalità di velocità perconst
e non è necessario scrivere casi di test per verificarlo in fase di runtime.La funzione trailing
const
oninspect()
member dovrebbe essere usata per indicare che il metodo non cambierà lo stato astratto (visibile al client) dell'oggetto . Ciò è leggermente diverso dal dire che il metodo non cambierà i "bit grezzi" della struttura dell'oggetto. I compilatori C ++ non possono interpretare "bitwise" a meno che non siano in grado di risolvere il problema di aliasing, che normalmente non può essere risolto (vale a dire che potrebbe esistere un alias non const che potrebbe modificare lo stato dell'oggetto). Un'altra (importante) intuizione di questo problema di aliasing: puntare a un oggetto con un puntatore a const non garantisce che l'oggetto non cambi; promette semplicemente che l'oggetto non cambierà tramite quel puntatore .