Nicht optionale Zeiger im Vergleich zu nicht konstanten Referenzen in C ++

12

In " Andere C ++ - Funktionen", "Referenzargumente" des Google C ++ - Stilhandbuchs , habe ich gelesen, dass Verweise, die keine Konstanten sind, nicht verwendet werden dürfen.

Alle als Referenz übergebenen Parameter müssen mit const gekennzeichnet sein.

Es ist klar, dass das Betrachten von Funktionsaufrufen, die Referenzen als Argumente verwenden, für C-Programmierer absolut verwirrend ist, aber C und C ++ sind jetzt verschiedene Sprachen. Wenn ein Ausgabeparameter erforderlich ist , kann die Verwendung eines Zeigers für einen erforderlichen Ausgabeparameter dazu führen, dass der gesamte Funktionskörper übersprungen wird, was die Implementierung einer Funktion komplizierter macht (was die zyklomatische Komplexität und Tiefe einer Funktion formal erhöht ).

Ich möchte, dass C ++ - Code so einfach wie möglich zu verstehen und zu warten ist. Daher bin ich generell daran interessiert, Coding-Style-Guides zu lesen. Um die Best Practices in einem Team anzupassen, ist es meiner Meinung nach ein wichtiger Faktor, die Hintergründe der Style-Guide-Elemente zu verstehen.

Sind nicht konstante Referenzen wirklich so schlecht? Ist das Verbot nur Google-spezifisch oder eine allgemein akzeptierte Regel? Was rechtfertigt den zusätzlichen Aufwand für die Implementierung von Ausgabeparametern als Zeiger?

Wolf
quelle
1
"Wenn ein Zeiger verwendet wird, wird der gesamte Funktionskörper übersprungen" äh, was?
Ratschenfreak
@ratchetfreak Ich habe versucht, dies zu klären. Ich gebe zu, dass Funktionen wie diese einige Designfehler aufweisen können. Ein Zeiger ist immer formal optional, daher muss er vor dem Dereferenzieren überprüft werden.
Wolf
3
Googles C ++ - Styleguide ist ziemlich rückwärts. Meiner subjektiven Meinung nach sollte es verbrannt werden.
Siyuan Ren
In Bezug auf diesen speziellen Punkt denke ich, dass die Begründung darin besteht, dass Programmierer gezwungen werden, ein kaufmännisches Und zu schreiben, wenn die Argumente mutiert werden können, um eine klarere Absicht zu zeigen.
Siyuan Ren
3
Der Google Style Guide wurde so geschrieben, wie er war, um homogenen Code in älteren Google-Projekten zu unterstützen. Wenn Sie nicht an älteren Projekten arbeiten (die von Anfang an mit diesem Styleguide geschrieben wurden), sollten Sie es wahrscheinlich nicht verwenden (es gibt viele Regeln an, die für neuen Code nicht geeignet sind (c ++ 11, c ++ 14) , c ++ 17)).
utnapistim

Antworten:

17

Das Grundprinzip hinter Googles Styleguide besteht einfach darin, auf der Aufrufseite einer Funktion zu verdeutlichen, ob ein Parameter ein Eingabeparameter oder ein Ausgabeparameter ist. ( Weitere Informationen hierzu finden Sie hier .) Andere Sprachen weisen Parameter auf, die ausdrücklich vorgesehen sind. C # hat beispielsweise ein outSchlüsselwort, das auf der Anrufseite verwendet werden muss . Da C ++ es nicht explizit macht, hat Google const ref gewählt. versus Zeiger, um es deutlich zu machen.

Ist das nur eine Google-Regel? Nein, aber ich bezweifle, dass es sehr verbreitet ist. Ich glaube nicht, dass ich es außerhalb des Google Style Guides und von Gruppen gesehen habe, die explizit Teile des Google Style Guides befolgen. (Zum Beispiel hat mir die Idee gefallen, als ich vor Jahren den Google Style Guide zum ersten Mal gelesen habe und ihn für einen Teil meines eigenen Codes verwendet habe.)

Insbesondere die neu angekündigten C ++ Core Guidelines bevorzugen Rückgabewerte als Ausgabeparameter für (fast) alles und verwenden im Übrigen Nicht-Konstanten-Refs. Googles Verwendung von Zeigern im Vergleich zu Referenzen macht die Ausgabeparameter möglicherweise klarer, die Rückgabewerte sind jedoch noch deutlicher. Da C ++ 11 standardisierte Schritte (rWert-Referenzen, &&um Rückgaben vieler Typen kostengünstig zu machen) und Tupel (die auf einfache Weise die Rückgabe mehrerer Werte ermöglichen) enthält, gelten viele der Anwendungsfälle für out-Parameter nicht mehr.

Die C ++ Core Guidelines haben einige große Namen (Bjarne Stroustrup, Herb Sutter) hinter sich, werden von Microsoft unterstützt und umfassen die neuesten C ++ - Funktionen (im Gegensatz zu Googles Style Guide), daher erwarte ich, dass die Empfehlungen bekannter sind als die von Google.

Josh Kelley
quelle
Vielen Dank für Ihre Antwort (auch für den kurzen Ausflug nach C #). Ein einfaches Reviewing ist natürlich ein wichtiger Punkt, insbesondere bei Open Source-Projekten. Mit günstigen Renditen in modernem C ++ werden diese Überlegungen an Bedeutung verlieren. Bei älteren Legacy-Programmen und älteren Compilern kann dies weiterhin hilfreich sein.
Wolf
Ich habe es nicht vergessen, aber C ++ Core Guidelines sind nicht so schnell zu bekommen. Es ist interessant, dass seine Philosophie die Hintergründe der Regeln und eine modernisierte Sichtweise der Programmierung ("Ideen direkt in Code ausdrücken" liest sich ein wenig wie das Zen von Python) als Kommunikationsmittel zeigt.
Wolf
1

Es gibt 2 Möglichkeiten, um mit einem ungültigen Zeiger umzugehen, zuerst zu prüfen und vorzeitig zurückzukehren oder es sich um undefiniertes Verhalten zu handeln (wenn Sie mehr Wert auf Geschwindigkeit als auf Robustheit legen).

Das Prüfen ist so einfach wie:

void foo(void* buffer){
    if(buffer == nullptr)
        return;

    //actually foo

    // note no increase in indentation required

}

Diese Art der Prüfung wird allgemein als Parameterprüfung akzeptiert. Wenn Sie den Code sehen, ist es ziemlich klar, dass Sie erwarten, dass ein Zeiger ungleich Null übergeben wird, und wenn nicht, früh zurückkehren. So können Sie sich weniger Gedanken über ungültige Zeiger machen.

Ratschenfreak
quelle
Nun, ich habe über dieses Muster nachgedacht und finde es absolut vernünftig. Leider sieht es nicht so klar aus wie zu assert(buffer);wissen, dass die Zusicherung nur für die Debug-Version aktiv ist. Manchmal möchte ich eine haben rt_assert(buffer);, die eine Ausnahme auslöst. Die Einrückung returnsieht ein wenig gefährlich aus ... Übrigens: Ihr Code-Snippet ist ein gutes Beispiel für meine Frage nach Zeigern für die Ausgabe.
Wolf
1

Es kommt auf Ihre Beobachtung an If an output parameter is required.

Die einzige Stelle, an der für eine Funktionssignatur ein Ausgabeparameter erforderlich ist, ist die Angabe durch eine externe API. In diesem Fall müssen Sie die externe API nur so einschließen, dass sichergestellt ist, dass immer ein gültiger Pointee vorhanden ist.

Intern vermeiden Sie Ausgabeparameter, indem Sie den Rückgabetyp so erweitern, dass er aus allen "Outs" besteht.

Caleth
quelle
Sie meinen, der einzige Ort, an dem ich keinen nicht obligatorischen Zeiger angeben kann? Stimmt, aber ich bin mir nicht sicher, ob Ihre Regel The only place where...wirklich auf alle Fälle anwendbar ist. Was Sie vorschlagen, sieht so aus: Vermeiden Sie Ausgabeparameter in den Funktionen Ihrer eigenen Programme. Richtig für neue Programme.
Wolf
Ja, Ausgabeparameter vermeiden, zusammengesetzte Rückgabetypen bevorzugen, um dies zu verdeutlichen
Caleth