übergabe - c++ string als referenz übergeben




Verwendung von 'const' für Funktionsparameter (20)

Wenn Sie die Operatoren ->* oder .* Verwenden, ist dies ein Muss.

Es verhindert, dass Sie etwas wie schreiben

void foo(Bar *p) { if (++p->*member > 0) { ... } }

was ich jetzt fast gemacht habe, und was wahrscheinlich nicht das tut, was Sie vorhaben.

Was ich sagen wollte war

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }

und wenn ich eine const zwischen Bar * und p hätte, hätte mir der Compiler das gesagt.

Wie weit gehst du mit const ? Machst du einfach Funktionen wenn nötig oder gehst du das ganze Schwein und benutzt es überall? Stellen Sie sich beispielsweise einen einfachen Mutator vor, der einen einzigen booleschen Parameter verwendet:

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

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

Ich war auch überrascht zu erfahren, dass Sie const aus Parametern in einer Funktionsdeklaration weglassen können, aber in die Funktionsdefinition aufnehmen können, zB:

.h-Datei

void func(int n, long l);

.cpp-Datei

void func(const int n, const long l)

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


Ah, ein harter. Auf der einen Seite ist eine Deklaration ein Vertrag und es macht wirklich keinen Sinn, ein const Argument nach Wert zu übergeben. Auf der anderen Seite, wenn Sie die Funktionsimplementierung betrachten, geben Sie dem Compiler mehr Möglichkeiten, um zu optimieren, wenn Sie eine Argumentkonstante deklarieren.



Const-Parameter sind nur dann nützlich, wenn der Parameter als Referenz übergeben wird, dh entweder als Referenz oder als Zeiger. Wenn der Compiler einen const-Parameter sieht, muss sichergestellt werden, dass die im Parameter verwendete Variable nicht im Rumpf der Funktion geändert wird. Warum sollte jemand einen By-Value-Parameter als Konstante definieren? :-)


Die Sache, an die man sich bei const erinnern sollte, ist, dass es viel einfacher ist, Dinge von Anfang an konstant zu machen, als sie später zu versuchen.

Verwenden Sie const, wenn Sie möchten, dass etwas unverändert bleibt - es ist ein zusätzlicher Hinweis, der beschreibt, was Ihre Funktion tut und was zu erwarten ist. Ich habe viele C-APIs gesehen, die mit einigen von ihnen umgehen könnten, besonders solche, die C-Strings akzeptieren!

Ich wäre eher geneigt, das const-Schlüsselwort in der cpp-Datei als die Kopfzeile wegzulassen, aber da ich dazu tendiere, sie zu schneiden und einzufügen, würden sie an beiden Orten behalten werden. Ich habe keine Ahnung, warum der Compiler das erlaubt, ich denke, es ist eine Compiler-Sache. Es empfiehlt sich, das Schlüsselwort const in beide Dateien zu schreiben.


Die folgenden zwei Zeilen sind funktional äquivalent:

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

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

Wo const wirklich nützlich ist, ist mit Referenz-oder Zeiger-Parameter:

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

Was das sagt, ist, dass foo einen großen Parameter annehmen kann, vielleicht eine Datenstruktur, die Gigabytes groß ist, ohne sie zu kopieren. Außerdem sagt es dem Anrufer: "Foo wird den Inhalt dieses Parameters nicht ändern." Durch die Übergabe einer Konst-Referenz kann der Compiler bestimmte Leistungsentscheidungen treffen.

*: Es sei denn, es wirft die Const-Ness weg, aber das ist ein weiterer Post.


Extra Überflüssige Const sind schlecht von einem API-Standpunkt:

Durch das Einfügen überflüssiger const-Codes in Ihren Code für intrinsische Typparameter, die als Wert übergeben werden, wird die API unübersichtlich, während dem Aufrufer oder API-Benutzer keine sinnvolle Zusage gemacht wird (dies behindert nur die Implementierung).

Zu viele "Const" in einer API, wenn nicht benötigt, ist wie " weinender Wolf ", schließlich werden die Leute beginnen, "const" zu ignorieren, weil es überall ist und die meiste Zeit nichts bedeutet.

Das Argument "reductio ad absurdum" für zusätzliche Consts in API ist gut für diese ersten beiden Punkte wäre, wenn mehr const Parameter gut sind, dann sollte jedes Argument, das eine const darauf haben kann, eine const darauf haben. In der Tat, wenn es wirklich so gut wäre, wäre const der Standard für Parameter und hätte ein Schlüsselwort wie "mutable" nur dann, wenn Sie den Parameter ändern wollen.

Also lass uns versuchen, 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 sicher ignoriert werden. Die zusätzliche Verwendung von "const" hat jedoch die zweite Zeile potenziell GEFÄHRLICH gemacht!

Warum?

Eine schnelle Fehllesung des ersten Parameters char * const buffer könnte Sie glauben machen, 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 diese schnell gescannt oder falsch gelesen werden.

Überflüssige const sind auch aus der Sicht der Code Implementation 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 zutrifft, dann ist die API "vielversprechend", die Funktion nicht wie unten beschrieben 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 machen, das Ihrem Anrufer überhaupt keinen Nutzen bringt und nur Ihre Implementierung einschränkt?

Beides sind vollkommen gültige Implementierungen der gleichen Funktion, aber alles, was Sie getan haben, ist eine Hand unnötig hinter Ihrem Rücken gebunden.

Darüber hinaus ist es ein sehr flaches Versprechen, das leicht (und rechtlich) 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);
}

Schau, ich habe es trotzdem so implementiert, obwohl ich es versprochen habe - nur mit einer Wrapper-Funktion. Es ist wie wenn der Bösewicht verspricht, jemanden in einem Film nicht zu töten und befiehlt seinem Handlanger, sie stattdessen zu töten.

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

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

Ich bin erleuchtet worden, dass Sie Const in Header (Deklaration) und Code (Definition) durch die Verwendung von falschen Const übereinstimmen können. Die Const-happy Befürworter behaupten, dies ist eine gute Sache, da Sie const nur in der Definition setzen können.

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

Allerdings ist das Umgekehrte wahr ... Sie können nur in der Deklaration ein falsches const setzen und es in der Definition ignorieren. Dies macht nur überflüssige Const in einer API zu einer schrecklichen Sache und einer schrecklichen Lüge - siehe dieses Beispiel:

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

All das überflüssige const macht tatsächlich den Code des Implementierers weniger lesbar, indem er ihn zwingt, eine andere lokale Kopie oder eine Wrapper-Funktion zu verwenden, wenn er die Variable ändern oder die Variable durch eine nicht-konstante Referenz übergeben möchte.

Schauen Sie sich dieses Beispiel an. Was ist lesbarer? Ist es offensichtlich, dass der einzige Grund für die zusätzliche Variable in der zweiten Funktion darin besteht, dass ein API-Designer einen überflüssigen const eingibt?

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üssige Const ist ein API-überladener Schandfleck, ein nervender Neger, ein oberflächliches und bedeutungsloses Versprechen, ein unnötiges Hindernis, und führt gelegentlich zu sehr gefährlichen Fehlern.


Ich benutze const, wo ich kann. Const für Parameter bedeutet, dass sie ihren Wert nicht ändern sollten. Dies ist besonders wertvoll, wenn Sie durch Verweis gehen. const for function erklärt, dass die Funktion die Klassenmitglieder nicht ändern soll.


Ich sage const deine Wertparameter.

Betrachten Sie diese Buggy-Funktion:

bool isZero(int number)
{
  if (number = 0)  // whoops, should be number == 0
    return true;
  else
    return false;
}

Wenn der Zahlparameter const wäre, würde der Compiler anhalten und uns vor dem Fehler warnen.


Ich verwende const nicht für Werte-übergebene Parameter. Dem Aufrufer ist es egal, ob Sie den Parameter ändern oder nicht, es handelt sich um ein Implementierungsdetail.

Es ist wirklich wichtig, Methoden als const zu markieren, wenn sie ihre Instanz nicht modifizieren. Tun Sie das so, wie Sie gehen, denn sonst könnten Sie entweder eine Menge const_cast <> erhalten, oder Sie könnten feststellen, dass das Markieren einer Methode const eine Menge Code erfordert, weil sie andere Methoden aufruft, die mit const gekennzeichnet sein sollten.

Ich neige auch dazu, lokale vars const zu markieren, wenn ich sie nicht modifizieren muss. Ich glaube, dass es den Code leichter verständlich macht, indem man die "beweglichen Teile" leichter identifizieren kann.


Ich würde const nicht auf Parameter wie diese setzen - jeder weiß bereits, dass ein Boolescher Wert (im Gegensatz zu einem booleschen &) konstant ist, also fügt das Hinzufügen von Werten die Leute dazu an zu denken "Warte, was?" oder sogar, dass Sie den Parameter als Referenz übergeben.


In dem Fall, den Sie erwähnen, betrifft dies nicht die Aufrufer Ihrer API, weshalb dies normalerweise nicht geschieht (und in der Kopfzeile nicht notwendig ist). Es betrifft nur die Implementierung Ihrer Funktion.

Es ist nicht besonders schlecht, aber die Vorteile sind nicht so groß, da es sich nicht auf Ihre API auswirkt, und es fügt Tipping hinzu, so dass es normalerweise nicht gemacht wird.


Manchmal (zu oft!) Muss ich den C ++ - Code einer anderen Person entwirren. Und wir alle wissen, dass der C ++ - Code von jemand anderem fast per Definition ein komplettes Durcheinander ist :) Also, das erste, was ich tue, um den lokalen Datenfluss zu entschlüsseln, ist const in jeder Variablendefinition, bis der Compiler zu bellen beginnt. Das bedeutet, dass auch value-Argumente kon- stant sind, da es sich nur um ausgefallene lokale Variablen handelt, die vom Aufrufer initialisiert werden.

Ah, ich wünschte, Variablen wären standardmäßig const und Mutable wurde für nicht-const Variablen benötigt :)


Vielleicht wird dies kein gültiges Argument sein. Aber wenn wir den Wert einer const-Variablen innerhalb eines Funktions-Compilers erhöhen, erhalten wir einen Fehler: " error: increment of read-only parameter ". Das bedeutet, dass wir const key word verwenden können, um zu verhindern, dass unsere Variablen versehentlich in Funktionen geändert werden (die wir nicht / nur lesen dürfen). Wenn wir es also versehentlich zur Kompilierzeit getan haben, wird uns der Compiler das mitteilen. Dies ist besonders wichtig, wenn Sie nicht der Einzige sind, der an diesem Projekt arbeitet.


Zusammenfassen:

  • "Normalerweise ist der Constant-Value-by-Value im besten Fall unbrauchbar und irreführend." Von here
  • Sie können sie jedoch wie bei Variablen in der .cpp-Datei hinzufügen.
  • Beachten Sie, dass die Standardbibliothek nicht const verwendet. ZB std::vector::at(size_type pos) . Was gut genug für die Standardbibliothek ist, ist gut für mich.

const ist sinnlos, wenn das Argument als Wert übergeben wird, da Sie das Objekt des Aufrufers nicht ändern.

const sollte bevorzugt werden, wenn es als Referenz übergeben wird, es sei denn, der Zweck der Funktion besteht darin, den übergebenen Wert zu ändern.

Schließlich kann und sollte eine Funktion, die das aktuelle Objekt (this) nicht modifiziert, const sein. Ein Beispiel ist unten:

int SomeClass::GetValue() const {return m_internalValue;}

Dies ist ein Versprechen, das Objekt, auf das dieser Aufruf angewendet wird, nicht zu ändern. Mit anderen Worten, Sie können anrufen:

const SomeClass* pSomeClass;
pSomeClass->GetValue();

Wenn die Funktion nicht const wäre, würde dies zu einer Compiler-Warnung führen.


"const ist sinnlos, wenn das Argument an Wert übergeben wird, da Sie das Objekt des Aufrufers nicht ändern werden."

Falsch.

Es geht darum, Ihren Code und Ihre Annahmen selbst zu dokumentieren.

Wenn Ihr Code viele Leute hat, die daran arbeiten und Ihre Funktionen nicht trivial sind, dann sollten Sie "const" markieren und alles, was Sie können. Wenn Sie industriellen Code schreiben, sollten Sie immer davon ausgehen, dass Ihre Mitarbeiter Psychopathen sind, die versuchen, Sie so weit wie möglich zu bringen (vor allem, weil Sie es oft selbst in der Zukunft sind).

Wie bereits erwähnt, könnte es dem Compiler dabei helfen, die Dinge ein wenig zu optimieren (obwohl es eine lange Geschichte ist).


All the consts in your examples have no purpose. C++ is pass-by-value by default, so the function gets copies of those ints and booleans. Even if the function does modify them, the caller's copy is not affected.

So I'd avoid extra consts because

  • They're redudant
  • They clutter up the text
  • They prevent me from changing the passed in value in cases where it might be useful or efficient.

Being a VB.NET programmer that needs to use a C++ program with 50+ exposed functions, and a .h file that sporadically uses the const qualifier, it is difficult to know when to access a variable using ByRef or ByVal.

Of course the program tells you by generating an exception error on the line where you made the mistake, but then you need to guess which of the 2-10 parameters is wrong.

So now I have the distasteful task of trying to convince a developer that they should really define their variables (in the .h file) in a manner that allows an automated method of creating all of the VB.NET function definitions easily. They will then smugly say, "read the ... documentation."

I have written an awk script that parses a .h file, and creates all of the Declare Function commands, but without an indicator as to which variables are R/O vs R/W, it only does half the job.

BEARBEITEN:

At the encouragement of another user I am adding the following;

Here is an example of a (IMO) poorly formed .h entry;

typedef int (EE_STDCALL *Do_SomethingPtr)( int smfID, const char* cursor_name, const char* sql );

The resultant VB from my script;

    Declare Function Do_Something Lib "SomeOther.DLL" (ByRef smfID As Integer, ByVal cursor_name As String, ByVal sql As String) As Integer

Note the missing "const" on the first parameter. Without it, a program (or another developer) has no Idea the 1st parameter should be passed "ByVal." By adding the "const" it makes the .h file self documenting so that developers using other languages can easily write working code.


I know the question is "a bit" outdated but as I came accross it somebody else may also do so in future... ...still I doubt the poor fellow will list down here to read my comment :)

It seems to me that we are still too confined to C-style way of thinking. In the OOP paradigma we play around with objects, not types. Const object may be conceptually different from a non-const object, specifically in the sense of logical-const (in contrast to bitwise-const). Thus even if const correctness of function params is (perhaps) an over-carefulness in case of PODs it is not so in case of objects. If a function works with a const object it should say so. Consider the following code snippet

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

ps.: you may argue that (const) reference would be more appropriate here and gives you the same behaviour. Well, right. Just giving a different picture from what I could see elsewhere...







const