Bedeutung von 'const' zuletzt in einer Funktionsdeklaration einer Klasse?


727

Was bedeuten constsolche Erklärungen? Das constverwirrt mich.

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

Antworten:


951

Wenn Sie das constSchlüsselwort zu einer Methode hinzufügen , wird der thisZeiger im Wesentlichen zu einem Zeiger auf ein constObjekt, und Sie können daher keine Mitgliedsdaten ändern. (Wenn Sie nicht verwenden mutable, dazu später mehr).

Das constSchlüsselwort ist Teil der Funktionssignatur. Dies bedeutet, dass Sie zwei ähnliche Methoden implementieren können, eine, die aufgerufen wird, wenn sich das Objekt befindet const, und eine, die nicht aufgerufen wird.

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

Dies wird ausgegeben

Foo
Foo const

In der non-const-Methode können Sie die Instanzmitglieder ändern, was in der constVersion nicht möglich ist. Wenn Sie die Methodendeklaration im obigen Beispiel in den folgenden Code ändern, werden einige Fehler angezeigt.

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

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

Dies ist nicht ganz richtig, da Sie ein Mitglied als markieren können mutableund eine constMethode es dann ändern kann. Es wird hauptsächlich für interne Zähler und ähnliches verwendet. Die Lösung dafür wäre der folgende Code.

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

was ausgeben würde

Foo
Foo const
Foo has been invoked 2 times


47

Das constQualifikationsmerkmal bedeutet, dass die Methoden für jeden Wert von aufgerufen werden können foobar. Der Unterschied ergibt sich, wenn Sie eine Nicht-Const-Methode für ein Const-Objekt aufrufen. Überlegen Sie, ob Ihr foobarTyp die folgende zusätzliche Methodendeklaration hatte:

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

Die Methode bar()ist nicht konstant und kann nur über nicht konstante Werte aufgerufen werden.

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

Die Idee dahinter constist jedoch, Methoden zu markieren, die den internen Zustand der Klasse nicht verändern. Dies ist ein leistungsstarkes Konzept, das in C ++ jedoch nicht durchsetzbar ist. Es ist eher ein Versprechen als eine Garantie. Und eine, die oft kaputt und leicht kaputt ist.

foobar& fbNonConst = const_cast<foobar&>(fb1);

3
Ich dachte, die Antwort bezieht sich auf andere const-Methoden und nicht auf const-Objekte.
Mykola Golubyev

Vielen Dank für die "Die Idee dahinter constist jedoch, Methoden zu markieren, die den internen Zustand der Klasse nicht verändern". Das habe ich wirklich gesucht.
Kovac

1
@JaredPar bedeutet dies, dass jede Mitgliedsfunktion, die eine schreibgeschützte Operation darstellt, als markiert werden sollte const?
Kovac

26

Diese const bedeuten, dass der Compiler einen Fehler macht, wenn die Methode 'with const' interne Daten ändert.

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

Die Prüfung

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
}

Lesen Sie dies für weitere Informationen


1
Eine Frage zu constMitgliedsfunktionen, in der nicht veränderlich erwähnt wird, ist bestenfalls unvollständig.
Unsichtbarer

13

Blairs Antwort ist genau richtig.

Beachten Sie jedoch, dass es ein mutableQualifikationsmerkmal gibt, das den Datenelementen einer Klasse hinzugefügt werden kann. Jedes so gekennzeichnete Mitglied kann in einer constMethode geändert werden, ohne den constVertrag zu verletzen .

Sie können dies (zum Beispiel) verwenden, wenn sich ein Objekt merken soll, wie oft eine bestimmte Methode aufgerufen wird, ohne die "logische" Konstanz dieser Methode zu beeinflussen.


10

Bedeutung einer Const-Member-Funktion in C ++ Allgemeines Wissen: Grundlegende Zwischenprogrammierung gibt eine klare Erklärung:

Der Typ dieses Zeigers in einer Nicht-Konstanten-Mitgliedsfunktion einer Klasse X ist X * const. Das heißt, es ist ein konstanter Zeiger auf ein nicht konstantes X (siehe Const-Zeiger und Zeiger auf Const [7, 21]). Da das Objekt, auf das sich dies bezieht, nicht const ist, kann es geändert werden. Der Typ in einer const-Member-Funktion einer Klasse X ist const X * const. Das heißt, es ist ein konstanter Zeiger auf eine Konstante X. Da das Objekt, auf das sich dies bezieht, const ist, kann es nicht geändert werden. Das ist der Unterschied zwischen konstanten und nicht konstanten Elementfunktionen.

Also in deinem Code:

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

Sie können es so denken:

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

thisist nicht const. Der Grund, warum es nicht geändert werden kann, ist, dass es ein Wert ist.
Brian

7

Wenn Sie constin der Methodensignatur (wie gesagt const char* foo() const;:) verwenden, teilen Sie dem Compiler mit, dass der Speicher, auf den durch verwiesen wird, thismit dieser Methode (die foohier angezeigt wird) nicht geändert werden kann .


6

Ich möchte den folgenden Punkt hinzufügen.

Sie können es auch zu einem const &und machenconst &&

Damit,

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
}

Fühlen Sie sich frei, um die Antwort zu verbessern. Ich bin kein Experte


1
*thisist immer ein l-Wert, auch wenn die Member-Funktion rvalue-ref-qualifiziert ist und für einen rvalue aufgerufen wird. Beispiel .
HolyBlackCat

1
Ja, wie soll ich meine aktuelle Antwort verbessern?
coder3101

Ich meine, was in einem Kommentar im Block zu schreiben ist, der das Verhalten rechtfertigt
coder3101

Aktualisiert. Ist das in Ordnung?
coder3101

2

Das mit der Funktionsdeklaration verwendete Schlüsselwort const gibt an, dass es sich um eine const-Member-Funktion handelt und die Datenelemente des Objekts nicht geändert werden können .


1

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

Was ist eine " constMitgliedsfunktion"?

Eine Mitgliedsfunktion, die ihr Objekt überprüft (anstatt es zu mutieren).

Eine constMitgliedsfunktion wird durch ein constSuffix direkt nach der Parameterliste der Mitgliedsfunktion angezeigt . Elementfunktionen mit einem constSuffix werden als "konstante Elementfunktionen" oder "Inspektoren" bezeichnet. constElementfunktionen ohne Suffix werden als "Nicht-Konstante-Elementfunktionen" oder "Mutatoren" bezeichnet.

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
}

Der Aufrufversuch unchangeable.mutate()ist ein Fehler, der beim Kompilieren abgefangen wird. Es gibt keinen Laufzeitspeicherplatz oder Geschwindigkeitsverlust für constund Sie müssen keine Testfälle schreiben, um dies zur Laufzeit zu überprüfen.

Die nacheilenden constauf inspect()Elementfunktion sollte die Methode bedeuten , die verwendet wird , nicht das Objekt ändern abstrakt (Client-sichtbar) Zustand. Dies unterscheidet sich geringfügig von der Aussage, dass die Methode die „Rohbits“ der Objektstruktur nicht ändert. C ++ - Compiler dürfen die "bitweise" Interpretation nur verwenden, wenn sie das Aliasing-Problem lösen können, das normalerweise nicht gelöst werden kann (dh es kann ein nicht konstanter Alias ​​vorhanden sein, der den Status des Objekts ändern kann). Eine weitere (wichtige) Erkenntnis aus diesem Aliasing-Problem: Das Zeigen auf ein Objekt mit einem Zeiger auf const garantiert nicht, dass sich das Objekt nicht ändert. es verspricht lediglich, dass sich das Objekt über diesen Zeiger nicht ändert .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.