Significato di "const" ultimo in una dichiarazione di funzione di una classe?


727

Qual è il significato di constin dichiarazioni come queste? Il constmi confonde.

class foobar
{
  public:
     operator int () const;
     const char* foo() const;
};

Risposte:


951

Quando aggiungi la constparola chiave a un metodo, il thispuntatore diventa essenzialmente un puntatore constall'oggetto e non puoi quindi modificare i dati dei membri. (A meno che non lo usi mutable, ne parleremo più avanti).

La constparola chiave fa parte della firma delle funzioni, il che significa che è possibile implementare due metodi simili, uno che viene chiamato quando l'oggetto è conste 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 constversione. 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 mutablee un constmetodo 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

187

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.


47

Il constqualificatore 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 foobartipo 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);

3
Pensavo che la risposta riguardasse altri metodi const e non oggetti const.
Mykola Golubyev,

Grazie per "L'idea alla base constè quella di segnare metodi che non altereranno lo stato interno della classe". Questo è davvero quello che stavo cercando.
Kovac,

1
@JaredPar significa che qualsiasi funzione membro che rappresenta un'operazione di sola lettura deve essere contrassegnata come const?
Kovac,

26

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


1
Una domanda sulle constfunzioni membro che non menziona mutevole è nella migliore delle ipotesi incompleta.
Indispensabile il

13

La risposta di Blair è nel segno.

Tuttavia, è presente un mutablequalificatore che può essere aggiunto ai membri dei dati di una classe. Qualsiasi membro così contrassegnato può essere modificato in un constmetodo senza violare il constcontratto.

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.


10

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;
};

thisnon lo è const. Il motivo per cui non può essere modificato è che si tratta di un valore.
Brian,

7

quando usi constnella firma del metodo (come hai detto const char* foo() const;:) stai dicendo al compilatore che la memoria indicata da thisnon può essere modificata con questo metodo (che è fooqui).


6

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


1
*thisè sempre un lvalue, anche se la funzione membro è qualificata rvalue-ref e viene chiamata su un rvalue. Esempio .
HolyBlackCat

1
Sì, quindi come devo migliorare la mia risposta attuale?
coder3101

Voglio dire cosa scrivere nel commento nel blocco, che giustifica il comportamento
coder3101

Aggiornato. Va bene?
coder3101,

2

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.


1

https://isocpp.org/wiki/faq/const-correctness#const-member-fns

Che cos'è una " constfunzione membro"?

Una funzione membro che controlla (piuttosto che muta) il suo oggetto.

Una constfunzione membro è indicata da un constsuffisso subito dopo l'elenco dei parametri della funzione membro. Le funzioni membro con un constsuffisso sono chiamate "funzioni membro const" o "ispettori". Le funzioni membro senza constsuffisso 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à per conste non è necessario scrivere casi di test per verificarlo in fase di runtime.

La funzione trailing conston inspect()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 .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.