Risposte:
Leggi questo: https://isocpp.org/wiki/faq/const-correctness
Il finale const
significa che la funzione Method3
non modifica i membri non modificabili della sua classe.
const int* const
indica un puntatore costante a una costante int: cioè un puntatore che non può essere modificato, a un int che non può essere modificato: l'unica differenza tra questo ed const int&
è che può esserenull
const int* const&
indica un riferimento a un puntatore costante a una costante int. Di solito i puntatori non vengono passati per riferimento; const int* &
ha più senso perché significherebbe che il puntatore potrebbe essere cambiato durante la chiamata al metodo, che sarebbe l'unica ragione per cui posso vedere per passare un puntatore per riferimento, const int* const&
è a tutti gli effetti lo stesso const int* const
tranne che probabilmente è meno efficiente poiché i puntatori sono tipi di dati semplici (POD) e questi dovrebbero, in generale, essere passati per valore.
È più facile da capire se lo riscrivi come completamente equivalente
// v───v───v───v───v───v───v───v───v───v───v───v─┬┐
// ││
// v──#1 v─#2 v──#3 v─#4 #5
int const * const Method3(int const * const&) const;
poi leggilo da destra a sinistra.
# 5 dice che l'intera dichiarazione di funzione a sinistra è const
, il che implica che questa è necessariamente una funzione membro piuttosto che una funzione libera.
# 4 dice che il puntatore a sinistra è const
(non può essere modificato per puntare a un indirizzo diverso).
# 3 dice che il int
a sinistra è const
(non può essere cambiato per avere un valore diverso).
# 2 dice che il puntatore a sinistra è const
.
# 1 dice che int
a sinistra è const
.
Mettendo tutto insieme, puoi leggerlo come una const
funzione membro denominata Method3
che prende un riferimento a un const
puntatore a un int const
(o a const int
, se preferisci) e restituisce un const
puntatore a un int const
( const int
).
(Nb # 2 è del tutto superfluo .)
Prima di tutto const T
è equivalente a T const
.
const int* const
è quindi equivalente a int const * const
.
Quando si leggono espressioni contenenti molti const
token e puntatori, provare sempre a leggerle da destra a sinistra (dopo aver applicato la trasformazione sopra). Quindi in questo caso il valore restituito è un puntatore const a una constint
. Fare il puntatore stesso const
non ha senso qui poiché il valore restituito non è un lvalue che potrebbe essere modificato. Fare le punte const
, tuttavia, garantisce che il chiamante non possa modificare il int
(o l'array di int
s) restituito da Method3
.
const int*const&
diventa int const*const&
, quindi è un riferimento a un puntatore const a un constint
. Anche il passaggio di un puntatore const tramite riferimenti male non ha senso: non è possibile modificare il valore di riferimento poiché il puntatore è const
e i riferimenti e i puntatori occupano lo stesso spazio di archiviazione, quindi non ci sono risparmi di spazio.
L'ultima const
indica che il metodo non modifica l' this
oggetto. Il this
puntatore all'interno del corpo del metodo avrà la dichiarazione (teorica) T const * const this
. Ciò significa che un const T*
oggetto potrà chiamare T::Method3()
.
const
s all'inizio della frase. Questo è precisamente il motivo per cui penso che sia una cattiva pratica da mettere const
lì, anche se la lingua lo consente, ed è l'uso più comune.
Un modo semplice per ricordare le regole di const
è pensarlo in questo modo: si const
applica alla cosa alla sua sinistra, a meno che non ci sia nulla alla sua sinistra.
Quindi, nel caso di const int * const
, la prima const non ha nulla alla sua sinistra, quindi si applica a int
e la seconda ha qualcosa alla sua sinistra, quindi si applica al puntatore.
Questa regola ti dice anche cosa succederebbe nel caso in cui lo hai const int const *
. Poiché entrambe le const si applicano a int
questa espressione è ridondante e quindi non valida.
const /* don't modify the int or array of ints' value(s) */
int* const /* as a retval, ignored. useless declaration */
Method3(const /* don't modify the int or array of ints' value(s) */
int* const /* don't modify the pointer's value, the address to which `pointer` points to. e.g. you cannot say `++pointer` */
&) const; /* this method does not modify the instance/object which implements the method */
Mi piace usare il metodo "orologio" o "spirale" in cui a partire dal nome dell'identificatore (in questo caso Method3
) leggi avanti e indietro da sinistra a destra-indietro a sinistra, ecc. Per decodificare convenzioni di denominazione. Quindi const int* const Method3(const int* const&) const
è un metodo di classe che non cambia alcun membro della classe (di qualche classe senza nome) e prende un riferimento costante a un puntatore che punta a una costante int
e restituisce un puntatore costante a una costanteint
.
Spero che questo ti aiuti,
Jason
Un modo semplice per ricordare il const in C ++ è quando vedi del codice in forma come:
XXX const;
const YYY;
XXX, YYY sarà una componente costante,
XXX const
form:
function ( def var ) const; ------#1
* const; ------#2
const YYY
modulo:
const int; ------#3
const double;
Le persone di solito usano questi tipi. Quando vedi "const&"
da qualche parte, non sentirti confuso, const sta descrivendo qualcosa prima di se stesso. quindi la risposta a questo problema è ovvia ora.
const int* const Method3(const int* const&) const;
| | | | |
#3 #2 #3 #2 #1
Voglio solo menzionare che const int* const&
è davvero un riferimento costante a const int*
. Per esempio:
int i = 0;
int j = 1;
int* p = &i;
int* q = &j;
const int* const& cpref = p;
cpref = q; //Error: assignment of read-only reference 'cpref'
È anche il caso di int* const&
, Che significa: "Un riferimento costante a int*
".
Ma const int*&
è un riferimento non costante a const int*
.
Spero che questo ti aiuti.
Leggere da destra a sinistra facilita la comprensione dei modificatori.
Un metodo const che accetta un riferimento a un puntatore const a un const int chiamato Method3
che restituisce un puntatore const a un const int.
mutable
)const # 1: il puntatore restituito da Method3 fa riferimento a un const int.
const # 2: il valore del puntatore restituito dalla funzione stessa è const. Questa è una const inutile (sebbene grammaticalmente valida), perché il valore restituito da una funzione non può essere un valore l.
const # 3: il tipo di puntatore passato per riferimento alla funzione punta a un const int.
const # 4: il valore del puntatore passato per riferimento alla funzione è di per sé un puntatore const. Dichiarare un valore che viene passato a una funzione come const sarebbe normalmente inutile, ma questo valore viene passato per riferimento, quindi può essere significativo.
const # 5: la funzione (presumibilmente una funzione membro) è const, il che significa che non è consentito (a) assegnare nuovi valori a nessun membro dell'oggetto di cui fa parte o (b) chiamare una funzione membro non const sull'oggetto o su uno dei suoi membri.
const
alla fine del metodo c'è il qualificatore che indica che lo stato dell'oggetto non verrà modificato.
const int*const&
significa ricevere per riferimento un puntatore const a una posizione const. Non può né cambiare per puntare a una posizione diversa né cambiare il valore a cui punta.
const int*const
è il valore di ritorno che è anche un puntatore costante a una posizione costante.
Alcuni esempi potrebbero essere utili per dimostrare questo concetto, più sono e meglio è.
class TestClass
{
private:
int iValue;
int* oValuePtr;
int& oValueRef;
public:
int TestClass::ByValMethod1(int Value)
{
// Value can be modified
Value++;
// iValue can be modified
iValue = Value;
iValue += 1;
// Return value can be modified
return ++iValue;
}
int TestClass::ByValMethod2(const int Value)
{
// Value *cannot* be modified
// Variable is const variable
Value++;
// iValue can be modified
iValue = Value;
iValue += 1;
// Return value can be modified
return ++iValue;
}
const int TestClass::ByValMethod3(int Value)
{
// Value can be modified
Value++;
// iValue can be modified
iValue = Value;
iValue += 1;
// Return value can be modified
return ++iValue;
}
const int TestClass::ByValMethod4(const int Value)
{
// Value *cannot* be modified
// Variable is const variable
Value++;
// iValue can be modified
iValue = Value;
iValue += 1;
// Return value can be modified
return ++iValue;
}
const int TestClass::ByValMethod5(const int Value) const
{
// Value *cannot* be modified
// Variable is const variable
Value++;
// iValue *cannot* be modified
// Access through a const object
iValue = Value;
iValue += 1;
// Return value *cannot* be modified
// Access through a const object
return ++iValue;
}
int& TestClass::ByRefMethod1(int& Value)
{
// Value can be modified
Value++;
// oValueRef can be modified
oValueRef = Value;
oValueRef += 1;
// Return value can be modified
return ++oValueRef;
}
int& TestClass::ByRefMethod2(const int& Value)
{
// Value *cannot* be modified
// Variable is const variable
Value++;
// oValueRef can be modified
oValueRef = Value;
oValueRef += 1;
// Return value can be modified
return ++oValueRef;
}
const int& TestClass::ByRefMethod3(int& Value)
{
// Value can be modified
Value++;
// oValueRef can be modified
oValueRef = Value;
oValueRef += 1;
// Return value can be modified
return ++oValueRef;
}
const int& TestClass::ByRefMethod4(const int& Value)
{
// Value *cannot* be modified
// Variable is const variable
Value++;
// oValueRef can be modified
oValueRef = Value;
oValueRef += 1;
// Return value can be modified
return ++oValueRef;
}
const int& TestClass::ByRefMethod5(const int& Value) const
{
// Value *cannot* be modified
// Variable is const variable
Value++;
// oValueRef can be modified
oValueRef = Value;
oValueRef += 1;
// Return value can be modified
return ++oValueRef;
}
int* TestClass::PointerMethod1(int* Value)
{
// Value can be modified
Value++;
// oValuePtr can be assigned
oValuePtr = Value;
// oValuePtr can be modified
oValuePtr += 1;
// Return value can be modified
return ++oValuePtr;
}
int* TestClass::PointerMethod2(const int* Value)
{
// Value can be modified
Value++;
// oValuePtr cannot be assigned
// const int* to int*
oValuePtr = Value;
// oValuePtr can be modified
oValuePtr += 1;
// Return value can be modified
return ++oValuePtr;
}
const int* TestClass::PointerMethod3(int* Value)
{
// Value can be modified
Value++;
// oValuePtr can be assigned
oValuePtr = Value;
// iValue can be modified
oValuePtr += 1;
// Return value can be modified
return ++oValuePtr;
}
const int* TestClass::PointerMethod4(const int* Value)
{
// Value cannot be modified
Value++;
// oValuePtr *cannot* be assigned
// const int* to int*
oValuePtr = Value;
// oValuePtr can be modified
oValuePtr += 1;
// Return value can be modified
return ++oValuePtr;
}
const int* TestClass::PointerMethod5(const int* Value) const
{
// Value can be modified
++Value;
// oValuePtr *cannot* be assigned
// const int* to int* const
// Access through a const object
oValuePtr = Value;
// oValuePtr *cannot* be modified
// Access through a const object
oValuePtr += 1;
// Return value *cannot* be modified
return ++oValuePtr;
}
};
Spero che aiuti!