Guter Stil in C++



  • hallo zusammen,

    ich hab mich grade wieder durch die suche gesucht 😃

    und bisschen was gelesen und immer lese ich ab und an´sachen wie das z.b:

    während es bei C++ guter Stil ist, nicht mit Pointern zu arbeiten.

    jetzt wollte ich mal fragen ob ihr nicht noch ein paar grundsätzliche Stil tips für mich habe?..

    mfg

    |23|



  • Und wie will man ohne Pointer z.B. verkette Liste realisieren. Ich halte die Aussage für schwachsinnig ⚠



  • pointer brauchst du in c++ immer.
    das hat m.E. nix mit stil zutun.

    zum weiteren: meinst du mit stil eher erscheinungsbild (einrückungen, varibalennamwahl, ...) oder programmierstil, richtung "design" (design patterns, ...)?



  • erscheinungsbild (einrückungen, varibalennamwahl, ...)`

    auch sowas:
    ich mal gelesen das,dass auch eine Sache ist :

    Guter Stil:

    int main(void);
    

    Schlechter Stil:

    int main();
    


  • Na ja 😃

    int main(void);

    Da kannst du das void doch weglassen, so ist das imho schlechter Stil.

    int main(); // Man sieht doch eh das es keine Parameter gibt

    Das (void) stammt doch eh noch aus der c zeit.



  • Ist doch totaler schwachsinn das dass schlechter Stil sein soll:

    int main();
    

    sowas würde ich persönlich als schwachsinn bezeichnen:

    void main(void)
    


  • Ich finde den Ratschlag mit den Pointern nicht so schlecht, auch wenn er vielleicht nicht sonderlich präzise formuliert ist. Ich denke das ist eher als Hinweis zu verstehen nach Möglichkeit was anderes zu verwenden. Tut's vielleicht ne Referenz? Oder kann man das Objekt direkt auf den Stack legen? Ich muß sagen meine Pointerbenutzung hat sich drastisch reduziert seit ich std::vector und Konsorten benutze... und das macht den Code einfacher und vermeidet Fehler. Dennoch ist klar, daß Pointer in C++ unverzichtbar sind und jeder C++-Programmierer wissen sollte, wie man damit umgeht.

    MfG Jester



  • während es bei C++ guter Stil ist, nicht mit Pointern zu arbeiten.

    schwachsinn.

    Schlechter Stil:
    int main();
    Guter Stil:
    int main(void);

    guter stil ist eh irgendwie schlecht zu greifen und der variiert auch je nach fähigkeiten und so.
    aber das mit den pointern ist eigentlich unfug. man meidet nicht pointer um des poitermeidens willen. wenn man mit der zeit besser wird, schafft man es mit der zeit, öfters mal an sich kompliziert aussehende sachen ganz einfach hinzuschreiben. dabei sparen sich auch pointer mal ein. aber ich denke an keinen fall, wo ich einer lösung mal den vorzug gewähren würde, bloß weil sie pointer meidet. fall der typ, der diese regel erfand, meine, man solle möglichst alle zeiger durch referenzen ersetzen, so ist er ganz auf dem holzweg. pointer und referenzen haben unterschiedliche bedeutungen.

    zum (void) oder () ist die sachen ganz klar. (void) ist schlechter stil. auch, wenn es in büchern oft anders steht. anders steht es noch aus der c-zeit.
    damals in c schrieb man (void) für eine funktion, die kein argument kriegen durfte, man schrieb () für eine funktion, die beliebig viele argumente kriegen durfte und wenn man mal nicht void ausgabe(void), sondern der faulheit wegen void ausgabe() geschrieben hatte, bekam man keine fehlermeldung, wenn man unten aus versehen ausgabe("warum sehe ich hier nix") aufgerufen hat. also ganz klare sache, in c war es guter stil, (void) statt () zu schreiben, wenn eine funktion keine parameter haben will. in c++ ist das jetzt anders. () sagt "will keine parameter". es gibt also keinen bedarf mehr, (void) zu schreiben. insofern wäre es egal. aber hinzu kommt, daß man in solchen egal-fällen das als guten stil bezeichnen muss, was die mehrheit macht. deine code wird besser lesbar, wenn du so schreibst, wie alle anderen.

    der komplette styleguid für c++ geht wohl etwa so:

    1. bevorzuge die sichere version
    2. sond beide versionen gleich, bevorzuge die üblich version

    früher gab es mal 2) bevorzuge die einfachere version und 3) bevorzuge die schnellere version. aber man hat entdeckt, daß die sichere version meistens auch die einfachere ist und daß darüberhinaus einfachheit meistens für schnelligkeit sorgt.



  • Ich zitier mal den final draft des C99-Standards dazu:

    5.1.2.2.1 Program startup

    [#1] The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:

    int main(void) { /* ... */ }

    or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):

    int main(int argc, char *argv[]) { /* ... */ }

    or equivalent; or in some other implementation-defined manner.

    Der FCD des C++-Standards dagegen meint:

    3.6.1 - Main function [basic.start.main]

    (...)

    -2- An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a return type of type int, but otherwise its type is implementation-defined. All implementations shall allow both of the following definitions of main:

    int main() { /* ... */ }

    and

    int main(int argc, char* argv[]) { /* ... */ }

    Also: In C ist int main(void) guter Stil, in C++ ist es int main().

    Was Pointer angeht, da gehen die Meinungen weit auseinander - C-Veteranen haben sich so an Pointer gewöhnt, dass sie damit keine Fehler mehr machen (oder zu machen glauben) und dementsprechend keinen Sinn darin sehen, auf sie zu verzichten. Ich für meinen Teil vermeide Pointer, wo es geht, und verwende Referenzen. Natürlich ist eine Referenz etwas anderes als ein Pointer, aber viele Probleme, für die man in C noch Pointer benutzt hätte, lassen sich in C++ gut mit Referenzen lösen (e.g. call-by-reference *sic* oder Variablen, die von mehreren Objekten gleichzeitig benutzt werden). Das ist zwar auch nicht hundertprozentig sicher - siehe z.B.

    std::string &foo() { std::string s; return s; }
    
    // ...
    
    std::cout << foo() << std::endl;
    

    Auch wenn das wahrscheinlich meistens funktionieren wird, ist s doch nach verlassen von foo ungültig, womit auch die Referenz ungültig wird - Vorsicht ist also auch da geboten. Das ist allerdings ein Fehler, der mit einem Pointer genau so aufträte, und meines Wissens führen Refrenzen keine neuen pitfalls ein. Natürlich gibt es diverse Situationen, in denen Pointer nicht vermieden werden können, und seltener gibt es auch Situationen, in denen Pointer zwar vermeidbar wären, es aber zu umständlich wäre - tendenziell würde ich Pointer dennoch als beliebte Fehlerquelle bezeichnen. Im Endeffekt bedeuetet das: Wenn es einen offensichtlichen Weg gibt, etwas ohne Pointer zu lösen, verzichte auf Pointer.



  • 0xdeadbeef schrieb:

    Im Endeffekt bedeuetet das: Wenn es einen offensichtlichen Weg gibt, etwas ohne Pointer zu lösen, verzichte auf Pointer.

    spassvogel.

    int& allocArray(){
       return &new int[10];
    }
    

    wo pointer gemeint sind, nimm pointer.

    std::string &foo() { std::string s; return s; }
    

    jo, hier referenzen. gemeint ist ja

    std::string foo() { std::string s; return s; }
    

    und wenn man einfach nur aus performancegründen auf ne kopie verzichtet, sind referenzen das angwesagte, denn sie fühlen sich an, wie das objekt selbst und die performance-sachen kann man sich wegdenken.



  • Ob Pointer gemeint sind, oder nicht, entscheidet sich aber genau da, wo du entscheidest, ob du pointer benutzen willst, oder nicht. Wenn du einmal ein Design gebaut hast, in dem du pointer brauchst, macht es natürlich keinen Sinn, auf Pointer zu verzichten, aber davon rede ich hier nicht.



  • Ob Pointer gemeint sind, oder nicht, entscheidet sich aber genau da, wo du entscheidest, ob du pointer benutzen willst, oder nicht.

    Nein.



  • Um...doch, ich denke schon, dass sich da, wo ich entscheide, was ich meine, entscheidet, was ich meine. Oder nicht?



  • volkard schrieb:

    int& allocArray(){
       return &new int[10];
    }
    

    Versteh irgendwie nicht, was du uns damit sagen willst. Müsste das aber nicht eher so

    int** allocArray()
    {
        return &new int[10];
    }
    

    oder so

    int& allocArray()
    {
        return *new int[10];
    }
    

    aussehen?


  • Mod

    wäre nicht

    int* allocArray()
    {
        return new int[10];
    }
    

    das offensichtliche? 😕
    mal abgesehen davon, dass man das funktionsergebnis ja straflos (bis auf ein speicherloch :p ) verwerfen könnte.



  • @groove volkard wollte mit dem beispiel wahrscheinlich nur zeigen, was eine referenz alles verschleiern kann.

    die referenz verschleiert nämlich in dem beispiel gleich 2 dinge:
    1. dass es ein array ist, referenzen können nicht auf den nächsten speicherbereich inkrementiert werden(was auch irgendwie logisch ist), und somit würde kein mensch darauf kommen, dass der rückgabewert ein array ist.

    2. allokierung über new:
    new gibt einen pointer zurück den man deleten kann-auf eine referenz kann man kein delete aufrufen, darum würde es wohl sehr weit entfernt liegen zu denken, dass diese int var über new erstellt wird.

    daraus folgt: wenn ich diese funktion aus einer dll benutzen würde, würd ich kein einziges mal delete aufrufen, und kein einziges mal mehr als ein Feld des zurückgelieferten arrays benutzen, dh ich hätte einerseits ein speicherleck, aber anderereits auch ne riesige resourcenverschwendung



  • otze schrieb:

    volkard wollte mit dem beispiel wahrscheinlich nur zeigen, was eine referenz alles verschleiern kann

    Das Problem ist, das sein Code einfach falsch ist. Deshalb kann ich mir darauf keinen Reim machen.

    otze schrieb:

    referenzen können nicht auf den nächsten speicherbereich inkrementiert werden

    Mit ein bissl tricksen geht das schon.

    otze schrieb:

    new gibt einen pointer zurück den man deleten kann-auf eine referenz kann man kein delete aufrufen, darum würde es wohl sehr weit entfernt liegen zu denken, dass diese int var über new erstellt wird.

    Klingt für mich noch am logischsten. Nur stellt sich dieses Problem für micht nicht, da ich es als falsches Design empfinde, innerhalb der Funktion Speicher zu reservieren und den Clent zu nötigen, diesen ausserhalb wieder freizugeben.



  • "Stil" fängt ganz unten an: Lesbarkeit, Pflegbarkeit, ein gewisses Maß an Ordnung, und die "richtigen Tools" im kleinen.

    Also das übliche Singsang:
    - Eindeutige konsistente Bezeichner für Variablen, Funktionen etc.,
    - konsistente Einrückung, die die Ablaufstruktur verdeutlicht,
    - Kommentare die
    (1) die Schnittstelle beschreiben,
    (2) das Ziel einzelner Code-Abschnitte bekanntgeben und
    (3) über ungewöhliche bzw. auf nicht offensichtlichen Randbedingungen beruhehnde Annahmen hinweghelfen,
    - Konsistenz in den "kleinen Regeln" (NULL oder 0 für Null-Zeiger, Fehlerbehandlung über Rückgabewert oder exception, ...)

    Das Zauberwort ist Konsistenz: Für ein großes Projekt ist der gewählte Stil weniger wichtig, als ihn wirklich durchzuziehen.

    Ordnung und richtige Tools - Das geht schon fließend in Designfragen über:

    - #defines nur wenn sich die Aufgabe nicht mit Sprachmitteln lösen läßt (einschließlich "const int oder enum statt #define"),
    - längere #defines durch inline/template-Hilfsfunktionen aufbrechen,
    - Initialisierung von Variablen
    - sinnvolle Kapselung (Klassen, aber auch Trennung Interface/Implementation)
    - sinnvolle verwendung von Stadard-Patterns
    - Resource Acquisition is Initialization (RAII) - s.u.

    (RAII - innerhalb einer Implementation ist das für mich "Stil", im Interface eine Frage des Designs. Hier kommen auch die Zeiger ins Spiel: Wenn du lokal Speicher brauchst, solltest du möglichst einen sinnvollen wrapper, z.B. Array oder Smart Pointer verwenden.)



  • groovemaster schrieb:

    otze schrieb:

    volkard wollte mit dem beispiel wahrscheinlich nur zeigen, was eine referenz alles verschleiern kann

    Das Problem ist, das sein Code einfach falsch ist. Deshalb kann ich mir darauf keinen Reim machen.

    jeder kann sich mal im zeichen irren, es gibt sowas,d ass nennt sich flüchtigkeitsfehler^^

    otze schrieb:

    referenzen können nicht auf den nächsten speicherbereich inkrementiert werden

    Mit ein bissl tricksen geht das schon.

    wenn du mit tricksen pointer meisnt, dann sind das immernoch keine referenzen, du würdest nur ausweichen^^

    otze schrieb:

    new gibt einen pointer zurück den man deleten kann-auf eine referenz kann man kein delete aufrufen, darum würde es wohl sehr weit entfernt liegen zu denken, dass diese int var über new erstellt wird.

    Klingt für mich noch am logischsten. Nur stellt sich dieses Problem für micht nicht, da ich es als falsches Design empfinde, innerhalb der Funktion Speicher zu reservieren und den Clent zu nötigen, diesen ausserhalb wieder freizugeben.

    sind Fabriken ein schlechtes Design?



  • Hat ja niemand gesagt, daß eine Class Factory einen "nackten" Pointer zurückgeben muß...



  • Genau, lieber ne Referenz. 😉 🤡 😃


Anmelden zum Antworten