Znaczenie „const” na końcu w deklaracji funkcji klasy?

727

Jakie jest znaczenie consttakich deklaracji? constMyli mnie.

class foobar
{
  public:
     operator int () const;
     const char* foo() const;
};
Rakete1111
źródło

Odpowiedzi:

951

Po dodaniu constsłowa kluczowego do metody thiswskaźnik zasadniczo stanie się wskaźnikiem do constobiektu, a zatem nie można zmienić żadnych danych elementu. (O ile nie użyjesz mutable, więcej o tym później).

Słowo constkluczowe jest częścią sygnatury funkcji, co oznacza, że ​​możesz zaimplementować dwie podobne metody, jedną wywoływaną, gdy obiekt jest const, a drugą nie.

#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();
}

To wyjdzie

Foo
Foo const

W metodzie innej niż const możesz zmienić członków instancji, czego nie można zrobić w constwersji. Jeśli zmienisz deklarację metody w powyższym przykładzie na poniższy kod, pojawi się kilka błędów.

    void Foo()
    {
        counter++; //this works
        std::cout << "Foo" << std::endl;    
    }

    void Foo() const
    {
        counter++; //this will not compile
        std::cout << "Foo const" << std::endl;
    }

Nie jest to do końca prawdą, ponieważ można oznaczyć członka jako, mutablea constnastępnie metoda może go zmienić. Najczęściej jest używany do liczników wewnętrznych i innych rzeczy. Rozwiązaniem tego byłby poniższy kod.

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

co by wydało

Foo
Foo const
Foo has been invoked 2 times
Mats Fredriksson
źródło
187

Const oznacza, że ​​metoda obiecuje nie zmieniać żadnych członków klasy. Będziesz w stanie wykonać elementy obiektu, które są tak oznaczone, nawet jeśli sam obiekt został oznaczony const:

const foobar fb;
fb.foo();

byłoby legalne.

Zobacz, ile i jakie są zastosowania „const” w C ++? po więcej informacji.

Blair Conrad
źródło
47

Te constśrodki kwalifikacyjne, że metody można nazwać na dowolnej wartości foobar. Różnica pojawia się, gdy rozważymy wywołanie metody innej niż const na obiekcie const. Zastanów się, czy Twój foobartyp ma następującą deklarację metody dodatkowej:

class foobar {
  ...
  const char* bar();
}

Metoda bar()nie jest stała i można uzyskać do niej dostęp wyłącznie z wartości innych niż stała.

void func1(const foobar& fb1, foobar& fb2) {
  const char* v1 = fb1.bar();  // won't compile
  const char* v2 = fb2.bar();  // works
}

Ideą constjest jednak oznaczenie metod, które nie zmienią wewnętrznego stanu klasy. Jest to potężna koncepcja, ale w rzeczywistości nie jest możliwa do wyegzekwowania w C ++. To bardziej obietnica niż gwarancja. I taki, który jest często łamany i łatwo łamany.

foobar& fbNonConst = const_cast<foobar&>(fb1);
JaredPar
źródło
3
Myślałem, że odpowiedź dotyczy innych metod const, a nie obiektów const.
Mykoła Golubjew
Dzięki za „Ideą constjest oznaczenie metod, które nie zmienią wewnętrznego stanu klasy”. Właśnie tego szukałem.
kovac,
1
@JaredPar oznacza, że ​​każda funkcja składowa reprezentująca operację tylko do odczytu powinna być oznaczona jako const ?
kovac,
26

Te const oznaczają, że kompilator wykona błąd, jeśli metoda „with const” zmieni dane wewnętrzne.

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

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
}

Przeczytaj to, aby uzyskać więcej informacji

Mykoła Golubiew
źródło
1
Pytanie o constfunkcje składowe, które nie wspomina o zmiennej, jest w najlepszym razie niepełne.
Widoczny
13

Odpowiedź Blair jest na znaku.

Należy jednak pamiętać, że istnieje element mutablekwalifikujący, który można dodać do elementów danych klasy. Każdy członek oznaczony w ten sposób może być modyfikowany constmetodą bez naruszaniaconst umowy.

Możesz użyć tego (na przykład), jeśli chcesz, aby obiekt pamiętał, ile razy jest wywoływana określona metoda, nie wpływając na „logiczną” stałość tej metody.

Alnitak
źródło
10

Znaczenie stałej funkcji członka w C ++ Common Knowledge: Essential Intermediate Programming daje jasne wyjaśnienie:

Typem tego wskaźnika w niestanowionej funkcji składowej klasy X jest X * const. Oznacza to, że jest to stały wskaźnik do niestałego X (patrz Const Wskaźniki i Wskaźniki do Const [7, 21]). Ponieważ obiekt, do którego się to odnosi, nie jest stały, można go modyfikować. Typem tego w stałej funkcji członka klasy X jest const X * const. Oznacza to, że jest to stały wskaźnik do stałej X. Ponieważ obiekt, do którego się to odnosi, jest const, nie można go modyfikować. Na tym polega różnica między stałymi i niepowiązanymi funkcjami składowymi.

W twoim kodzie:

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

Możesz myśleć tak:

class foobar
{
  public:
     operator int (const foobar * const this) const;
     const char* foo(const foobar * const this) const;
};
Nan Xiao
źródło
thisnie jest const. Powodem, dla którego nie można go modyfikować, jest to, że jest to wartość.
Brian,
7

kiedy używasz constw sygnaturze metody (jak powiedziałeś const char* foo() const;:), mówisz kompilatorowi, że wskazanej przez pamięć pamięci thisnie można zmienić tą metodą (która jest footutaj).

Matrix Buster
źródło
6

Chciałbym dodać następujący punkt.

Możesz także zrobić to const &aconst &&

Więc,

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
}

Popraw odpowiedź. Nie jestem ekspertem

koder3101
źródło
1
*thisjest zawsze wartością, nawet jeśli funkcja składowa ma kwalifikację-wartości-referencyjnej i jest wywoływana na wartości. Przykład .
HolyBlackCat
1
Tak, więc jak poprawić obecną odpowiedź?
coder3101
Mam na myśli to, co napisać w komentarzu w bloku, który usprawiedliwia zachowanie
koder3101
Zaktualizowano Czy to jest ok?
coder3101
2

Const kluczowe używany z Określa deklaracji funkcji, że jest to funkcja członkiem const i to będzie nie być w stanie zmienić członków danych obiektu.

Chandra Shekhar
źródło
1

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

Co to jest „ constfunkcja członka”?

Funkcja składowa, która sprawdza (zamiast mutuje) swój obiekt.

Funkcja constczłonka jest oznaczona constsufiksem tuż za listą parametrów funkcji członka. Funkcje constskładowe z sufiksem nazywane są „stałymi funkcjami składowymi” lub „inspektorami”. Funkcje constskładowe bez sufiksu nazywane są „funkcjami składowymi niepowiązanymi” lub „mutatorami”.

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
}

Próba połączenia unchangeable.mutate()jest błędem wychwyconym podczas kompilacji. Nie ma miejsca na czas wykonywania ani ograniczenia prędkości consti nie trzeba pisać przypadków testowych, aby sprawdzić to w czasie wykonywania.

Funkcja końcowa constna inspect()elemencie członkowskim powinna być używana w celu oznaczenia, że ​​metoda nie zmieni abstrakcyjnego stanu obiektu (widocznego dla klienta). To nieco różni się od stwierdzenia, że ​​metoda nie zmieni „surowych bitów” struktury obiektu. Kompilatory C ++ nie mogą przyjmować interpretacji „bitowej”, chyba że potrafią rozwiązać problem aliasingu, którego normalnie nie można rozwiązać (tzn. Nie istniałby stały alias, który mógłby modyfikować stan obiektu). Kolejny (ważny) wgląd w ten problem aliasingu: wskazywanie na obiekt wskaźnikiem do stałej nie gwarantuje, że obiekt się nie zmieni; po prostu obiecuje, że obiekt nie zmieni się za pomocą tego wskaźnika .

qwr
źródło