Verwendung von ‘const’ für Funktionsparameter

Lesezeit: 12 Minuten

Verwendung von const fur Funktionsparameter
rauben

Wie weit gehst du mit const? Machen Sie nur Funktionen const bei Bedarf oder gehst du aufs Ganze und verwendest es überall? Stellen Sie sich zum Beispiel einen einfachen Mutator vor, der einen einzelnen booleschen Parameter akzeptiert:

void SetValue(const bool b) { my_val_ = b; }

Ist das const eigentlich sinnvoll? Persönlich entscheide ich mich dafür, es ausgiebig zu verwenden, einschließlich Parameter, aber in diesem Fall frage ich mich, ob es sich lohnt?

Ich war auch überrascht zu erfahren, dass Sie weglassen können const von Parametern in einer Funktionsdeklaration, kann es aber in die Funktionsdefinition aufnehmen, z.

.h-Datei

void func(int n, long l);

.cpp-Datei

void func(const int n, const long l)

Gibt es dafür einen Grund? Es kommt mir etwas ungewöhnlich vor.

  • Ich bin nicht einverstanden. Die .h-Datei muss auch die const-Definitionen enthalten. Wenn dies nicht der Fall ist, generiert der Compiler einen Fehler, wenn const-Parameter an die Funktion übergeben werden, da der Prototyp in der .h-Datei nicht über die const-Definitionen verfügt.

    – Selwyn

    25. September 2008 um 20:31 Uhr

  • Ich stimme zu. 🙂 (Bei der Frage, nicht der letzte Kommentar!) Wenn ein Wert im Rumpf der Funktion nicht geändert werden soll, kann dies helfen, dumme == oder = Fehler zu stoppen, Sie sollten niemals const in beide einfügen (wenn es wird als Wert übergeben, Sie müssen es sonst tun) Es ist jedoch nicht ernst genug, um darüber zu streiten!

    – Chris Huang-Leaver

    20. April 2009 um 14:24 Uhr

  • @selwyn: Selbst wenn Sie eine Konstante int an die Funktion übergeben, wird sie kopiert (da es sich nicht um eine Referenz handelt), und daher spielt die Konstante keine Rolle.

    – jalf

    4. Mai 2009 um 23:20 Uhr

  • Dieselbe Debatte findet in dieser Frage statt: stackoverflow.com/questions/1554750/…

    – Teilweise

    12. November 2009 um 15:34 Uhr

  • Mir ist klar, dass dieser Beitrag ein paar Jahre alt ist, aber als neuer Programmierer habe ich mir genau diese Frage gestellt und bin über diese Unterhaltung gestolpert. Meiner Meinung nach sollte eine Funktion, wenn sie einen Wert nicht ändern soll, egal ob es sich um eine Referenz oder eine Kopie des Werts/Objekts handelt, const sein. Es ist sicherer, selbstdokumentierend und debugfreundlicher. Selbst für die einfachste Funktion, die eine Anweisung hat, verwende ich immer noch const.

    Benutzer898058

    19. September 2011 um 20:45 Uhr

1647166214 416 Verwendung von const fur Funktionsparameter
Greg Rogers

Der Grund ist, dass const denn der Parameter gilt nur lokal innerhalb der Funktion, da auf einer Kopie der Daten gearbeitet wird. Dies bedeutet, dass die Funktionssignatur sowieso wirklich dieselbe ist. Es ist aber wahrscheinlich schlechter Stil, dies oft zu tun.

Ich persönlich neige dazu, es nicht zu verwenden const mit Ausnahme von Referenz- und Zeigerparametern. Für kopierte Objekte spielt es keine Rolle, obwohl es sicherer sein kann, da es die Absicht innerhalb der Funktion signalisiert. Es ist wirklich ein Urteilsspruch. Ich neige dazu, zu verwenden const_iterator Wenn ich jedoch etwas loope und ich nicht beabsichtige, es zu ändern, denke ich, jedem das Seine, solange const Korrektheit für Referenztypen wird streng eingehalten.

  • Ich kann dem Teil “schlechter Stil” nicht zustimmen. Fallenlassen const von Funktionsprototypen hat den Vorteil, dass Sie die Header-Datei nicht ändern müssen, wenn Sie sich entscheiden, sie zu löschen const aus dem Implementierungsteil später.

    – Michał Górny

    9. Januar 2010 um 11:53 Uhr

  • “Ich persönlich neige dazu, const nur für Referenz- und Zeigerparameter zu verwenden.” Vielleicht sollten Sie das verdeutlichen: „Ich neige dazu, keine überflüssigen Qualifizierer in Funktionsdeklarationen zu verwenden, sondern benutze const wo es einen nützlichen Unterschied macht.”

    – Deduplizierer

    9. September 2014 um 16:53 Uhr

  • Ich bin mit dieser Antwort nicht einverstanden. Ich lehne mich in die andere Richtung und markiere Parameter const wenn möglich; es ist ausdrucksstärker. Wenn ich den Code eines anderen lese, verwende ich kleine Indikatoren wie diesen, um zu beurteilen, wie viel Sorgfalt sie beim Schreiben ihres Codes aufwenden, neben Dingen wie magischen Zahlen, Kommentaren und der richtigen Verwendung von Zeigern usw.

    – Ultimater

    1. August 2016 um 20:27 Uhr

  • int getDouble(int a){ ++a; return 2*a; } Versuche dies. Natürlich die ++a hat damit nichts zu tun kann finden Sie dort in einer langen Funktion, die von mehr als einem Programmierer über einen langen Zeitraum geschrieben wurde. Ich würde dringend empfehlen zu schreiben int getDouble( const int a ){ //... } das wird beim Finden einen Kompilierfehler erzeugen ++a;.

    – dom_beau

    27. März 2017 um 3:58 Uhr


  • Es kommt darauf an, wer welche Informationen benötigt. Sie geben den Parameter an nach Wert Also der Anrufer muss nichts wissen darauf, was Sie (intern) damit machen. Also schreiben class Foo { int multiply(int a, int b) const; } in deiner Kopfzeile. In Ihrer Umsetzung es kümmert dich dass Sie versprechen können, sich nicht zu ändern a und b damit int Foo::multiply(const int a, const int b) const { } macht hier Sinn. (Nebenbemerkung: Sowohl der Aufrufer als auch die Implementierung kümmern sich darum, dass die Funktion ihre nicht ändert Foo Objekt, also die Konstante am Ende seiner Deklaration)

    – CharonX

    1. Oktober 2018 um 9:54 Uhr

  • “Ich wünschte, Variablen wären standardmäßig konstant” – ein Oxymoron?? 😎 Im Ernst, wie hilft Ihnen das “Consting” von allem, Code zu entwirren? Wenn der ursprüngliche Autor ein angeblich konstantes Argument geändert hat, woher wissen Sie dann, dass die Variable eine Konstante sein sollte? Darüber hinaus soll die überwiegende Mehrheit der (Nicht-Argument-) Variablen … Variablen sein. Der Compiler sollte also sehr bald nach dem Start des Prozesses abbrechen, oder?

    – jap

    23. Januar 2017 um 7:59 Uhr


  • @ysap, 1. Wenn ich const so oft wie möglich markiere, kann ich sehen, welche Teile sich bewegen und welche nicht. Meiner Erfahrung nach sind viele Einheimische de facto konstant, nicht umgekehrt. 2. „Konstante Variable“/„Unveränderliche Variable“ mag wie ein Oxymoron klingen, ist aber Standardpraxis in funktionalen Sprachen sowie einigen nicht-funktionalen; siehe Rost zum Beispiel: doc.rust-lang.org/book/variable-bindings.html

    – Konstantin

    29. Januar 2017 um 16:49 Uhr

  • In C++ unter Umständen jetzt auch Standard; zum Beispiel das Lambda [x](){return ++x;} ist ein Fehler; siehe hier

    – anatolyg

    21. Juni 2017 um 14:08 Uhr

  • Variablen sind “const” standardmäßig in Rost 🙂

    – Phönix

    31. Juli 2017 um 12:46 Uhr


  • @phoenix Für den zukünftigen Leser: Warum wird Unveränderlichkeit in Rust erzwungen, sofern nicht anders angegeben mit mut?

    – ynn

    27. April 2020 um 21:37 Uhr

Die folgenden beiden Zeilen sind funktional gleichwertig:

int foo (int a);
int foo (const int a);

Offensichtlich können Sie nicht ändern a im Körper von foo wenn es auf die zweite Art definiert ist, aber es gibt keinen Unterschied von außen.

Woher const wirklich praktisch ist mit Referenz- oder Zeigerparametern:

int foo (const BigStruct &a);
int foo (const BigStruct *a);

Das bedeutet, dass foo einen großen Parameter, vielleicht eine Datenstruktur mit einer Größe von Gigabyte, annehmen kann, ohne sie zu kopieren. Außerdem sagt es dem Anrufer: “Foo wird den Inhalt dieses Parameters nicht ändern.” Durch das Übergeben einer const-Referenz kann der Compiler auch bestimmte Leistungsentscheidungen treffen.

*: Es sei denn, es wirft die Konstanz weg, aber das ist ein anderer Beitrag.

  • Darum geht es in dieser Frage nicht; natürlich ist es für Argumente, auf die verwiesen wird oder auf die gezeigt wird, eine gute Idee, const zu verwenden (wenn der Wert, auf den verwiesen wird oder auf den gezeigt wird, nicht geändert wird). Beachten Sie, dass dies nicht der Fall ist Parameter das ist const in Ihrem Zeigerbeispiel; es ist das, worauf der Parameter hinweist.

    – Tml

    5. Mai 2016 um 9:12 Uhr

  • > Durch das Übergeben einer const-Referenz kann der Compiler auch bestimmte Leistungsentscheidungen treffen. Klassischer Fehlschluss – der Compiler muss die Konstantheit selbst bestimmen, das const-Schlüsselwort hilft dank Pointer-Aliasing und const_cast nicht weiter

    – jheriko

    4. Oktober 2016 um 16:54 Uhr

1646724615 488 Was ist der Unterschied zwischen stdmove und stdforward
Enliko

Extra überflüssige Konstanten sind aus API-Sicht schlecht:

Einfügen zusätzlicher überflüssiger Konstanten in Ihren Code für intrinsische Typparameter, die als Wert übergeben werden verstopft Ihre API während es dem Aufrufer oder API-Benutzer kein sinnvolles Versprechen gibt (es behindert nur die Implementierung).

Zu viele ‘const’ in einer API, wenn sie nicht benötigt werden, sind wie “heulender Wolf“, irgendwann werden die Leute anfangen, ‘const’ zu ignorieren, weil es überall herumliegt und die meiste Zeit nichts bedeutet.

Das Argument “reductio ad absurdum” für zusätzliche Konstanten in der API ist gut für diese ersten beiden Punkte. Wenn mehr konstante Parameter gut sind, sollte jedes Argument, das eine Konstante enthalten kann, eine Konstante enthalten. Wenn es wirklich so gut wäre, möchten Sie, dass const der Standardwert für Parameter ist und ein Schlüsselwort wie “mutable” nur dann hat, wenn Sie den Parameter ändern möchten.

Versuchen wir also, const einzugeben, wo immer wir können:

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);

Betrachten Sie die obige Codezeile. Die Deklaration ist nicht nur überladener und länger und schwerer zu lesen, sondern drei der vier „const“-Schlüsselwörter können vom API-Benutzer getrost ignoriert werden. Die zusätzliche Verwendung von ‘const’ hat die zweite Zeile jedoch potenziell gemacht GEFÄHRLICH!

Warum?

Eine schnelle Fehlinterpretation des ersten Parameters char * const buffer könnte Sie denken lassen, dass der Speicher im Datenpuffer, der übergeben wird, nicht geändert wird – dies ist jedoch nicht wahr! Überflüssiges „const“ kann zu gefährlichen und falschen Annahmen über Ihre API führen wenn sie schnell gescannt oder falsch gelesen werden.


Überflüssige Konstanten sind auch aus Sicht der Code-Implementierung schlecht:

#if FLEXIBLE_IMPLEMENTATION
       #define SUPERFLUOUS_CONST
#else
       #define SUPERFLUOUS_CONST             const
#endif

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count);

Wenn FLEXIBLE_IMPLEMENTATION nicht wahr ist, „verspricht“ die API, die Funktion nicht auf dem ersten Weg unten zu implementieren.

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       // Will break if !FLEXIBLE_IMPLEMENTATION
       while(count--)
       {
              *dest++=*source++;
       }
}

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       for(int i=0;i<count;i++)
       {
              dest[i]=source[i];
       }
}

Das ist ein sehr dummes Versprechen. Warum sollten Sie ein Versprechen geben, das Ihrem Anrufer keinerlei Nutzen bringt und Ihre Umsetzung nur einschränkt?

Beides sind jedoch vollkommen gültige Implementierungen derselben Funktion, sodass Sie lediglich eine Hand unnötig hinter Ihrem Rücken gefesselt haben.

Außerdem ist es ein sehr oberflächliches Versprechen, das leicht (und legal) umgangen werden kann.

inline void bytecopyWrapped(char * dest,
   const char *source, int count)
{
       while(count--)
       {
              *dest++=*source++;
       }
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source,SUPERFLUOUS_CONST int count)
{
    bytecopyWrapped(dest, source, count);
}

Sehen Sie, ich habe es trotzdem so implementiert, obwohl ich versprochen hatte, es nicht zu tun – nur mit einer Wrapper-Funktion. Es ist, als würde der Bösewicht in einem Film versprechen, niemanden zu töten, und stattdessen seinem Handlanger befehlen, ihn zu töten.

Diese überflüssigen Konstanten sind nicht mehr wert als ein Versprechen eines Film-Bösewichts.


Aber die Fähigkeit zu lügen wird noch schlimmer:

Mir wurde aufgeklärt, dass Sie const in Header (Deklaration) und Code (Definition) falsch abgleichen können, indem Sie falsche const verwenden. Die Befürworter von konstanten Freuden behaupten, dies sei eine gute Sache, da Sie const nur in die Definition aufnehmen können.

// Example of const only in definition, not declaration
struct foo { void test(int *pi); };
void foo::test(int * const pi) { }

Das Gegenteil ist jedoch wahr … Sie können eine falsche Konstante nur in die Deklaration einfügen und sie in der Definition ignorieren. Dies macht const in einer API nur überflüssig und eher eine schreckliche Sache und eine schreckliche Lüge – siehe dieses Beispiel:

struct foo
{
    void test(int * const pi);
};

void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
    pi++;  // I promised in my definition I wouldn't modify this
}

Das überflüssige const macht den Code des Implementierers weniger lesbar, indem es ihn dazu zwingt, eine andere lokale Kopie oder eine Wrapper-Funktion zu verwenden, wenn er die Variable ändern oder die Variable als Nicht-Konstanten-Referenz übergeben möchte.

Sehen Sie sich dieses Beispiel an. Was ist besser lesbar? Ist es offensichtlich, dass der einzige Grund für die zusätzliche Variable in der zweiten Funktion darin besteht, dass ein API-Designer eine überflüssige Konstante eingefügt hat?

struct llist
{
    llist * next;
};

void walkllist(llist *plist)
{
    llist *pnext;
    while(plist)
    {
        pnext=plist->next;
        walk(plist);
        plist=pnext;    // This line wouldn't compile if plist was const
    }
}

void walkllist(llist * SUPERFLUOUS_CONST plist)
{
    llist * pnotconst=plist;
    llist *pnext;
    while(pnotconst)
    {
        pnext=pnotconst->next;
        walk(pnotconst);
        pnotconst=pnext;
    }
}

Hoffentlich haben wir hier etwas gelernt. Überflüssiger const ist ein API-überladener Schandfleck, ein nerviger Nörgler, ein seichtes und nichtssagendes Versprechen, ein unnötiges Hindernis und führt gelegentlich zu sehr gefährlichen Fehlern.

  • Darum geht es in dieser Frage nicht; natürlich ist es für Argumente, auf die verwiesen wird oder auf die gezeigt wird, eine gute Idee, const zu verwenden (wenn der Wert, auf den verwiesen wird oder auf den gezeigt wird, nicht geändert wird). Beachten Sie, dass dies nicht der Fall ist Parameter das ist const in Ihrem Zeigerbeispiel; es ist das, worauf der Parameter hinweist.

    – Tml

    5. Mai 2016 um 9:12 Uhr

  • > Durch das Übergeben einer const-Referenz kann der Compiler auch bestimmte Leistungsentscheidungen treffen. Klassischer Fehlschluss – der Compiler muss die Konstantheit selbst bestimmen, das const-Schlüsselwort hilft dank Pointer-Aliasing und const_cast nicht weiter

    – jheriko

    4. Oktober 2016 um 16:54 Uhr

Verwendung von const fur Funktionsparameter
QBziZ

const sollte der Standard in C++ sein. So was :

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable

  • Die Kompatibilität mit C ist zu wichtig, zumindest für die Leute, die C++ entwickeln, um dies auch nur in Betracht zu ziehen.

    Roger Pate

    16. März 2010 um 4:57 Uhr

  • Interessant, daran hatte ich noch nie gedacht.

    – Dan

    18. September 2012 um 20:56 Uhr

  • Ähnlich, unsigned sollte der Standard in C++ sein. So was: int i = 5; // i is unsigned und signed int i = 5; // i is signed.

    – hk Battousai

    21. Juli 2015 um 13:49 Uhr

997110cookie-checkVerwendung von ‘const’ für Funktionsparameter

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy