Einleitung
Diese Konventionen sind als Anhaltspunkte und Richtlinien zur Stilbildung zu verstehen. Im Zweifelsfall sollten
sie herangezogen werden, um eine Entscheidung zwischen mehreren möglichen Alternativen zu ermöglichen. Dies
ist als Service gedacht, um die Vergabe (oder den Abzug) von Punkten aufgrund von Stilfragen fair und nachvollziehbar
zu halten. Es muss nicht pedantisch jedes einzelne Statement auf Übereinstimmung mit allen Punkten überprüft
werden, aber grobe Abweichungen (d.h. massive oder wiederholt auftretende, oder komplette Missachtungen einzelner Punkte)
sollten vermieden werden.
Einrückung und Leerzeichen
Klammern sollen im "Exdented Style" gehalten sein, d.h. sie werden nicht mit eingerückt. Ausdrücke innerhalb der
Klammern werden relativ zu diesen eingerückt.
Einrückung besteht aus 4 Leerzeichen vom Beginn der umschließenden Deklaration oder des Ausdrucks an, und sollte
mit Tabs erfolgen (Tab-Weite im Editor entsprechend einstellen).
Beispiel:
|
void f(int a)
{
int i;
if (a > 0)
{
i = a;
foo = bar;
}
else
i = -a;
}
|
Weitere mögliche Varianten:
|
void f(int a) {
int i;
if (a > 0) {
i = a;
foo = bar;
}
else
i = -a;
}
|
|
void f(int a)
{
int i;
if (a > 0)
{
i = a;
foo = bar;
}
else
i = -a;
}
|
Jeder Ausdruck soll (für gewöhnlich) auf einer eigenen Zeile stehen.
Code muss nicht um jeden Preis kompakt gemacht werden. Mehrere Statements auf einer Zeile
erschweren es, den Code zu lesen.
Alle binären arithmetischen, Bitoperatoren und Zuweisungsoperatoren
sowie der Konditional-Operator (?:) sollen links und rechts von genau einem Leerzeichen umgeben
sein; dem Komma-Operator soll ein Leerzeichen folgen, aber nicht vorangehen; alle
anderen Operatoren sollen ohne Leerraum benutzt werden.
Zeilen sollen die Länge von 80 Zeichen nicht überschreiten.
Dies ist Konvention. Ebenfalls nützlich beim Ausdrucken.
Blöcke
Anweisungsblöcke sollen nur eingeführt werden, wenn sie wirklich nötig sind.
Die einzelnen Anweisungen stehen dann direkt unter der if-Abfrage (for-Schleife, while-Schleife, etc.)
Beispiel:
|
void f(int a)
{
int i;
if (a > 0)
i = a;
else
i = -a;
}
|
anstatt
Beispiel (falsch):
|
void f(int a)
{
int i;
if (a > 0)
{
i = a;
}
else
{
i = a;
}
}
|
Man kann hier einwenden, daß dies beim späteren Editieren Probleme verursachen könnte, wenn
man nicht darauf achtet, notwendig gewordene Klammern hinzuzufügen. Ich gehe davon aus, daß man
dies in dem Fall sofort an der automatischen Einrückung durch den Editor ersehen kann.
Namensgebung
Namen sind komplett in Kleinbuchstaben gehalten, und Wörter sind durch einen Unterstrich (_) voneinander getrennt.
Beispiel: int max_points, int current_sum;
Konstanten bestehen dagegen ausschließlich aus Großbuchstaben und
dem Unterstrich.
Beispiel: const int MAX_INPUT_SIZE = 50;
Klassennamen beginnen mit einem Großbuchstaben, Namen von Objekten dagegen (wie bereits gesagt) mit einem Kleinbuchstaben.
Beispiel: Vector my_vector;
Namen
Eingängige, beschreibende Namen verwenden.
Temporär eingeführte Variablen (z.B. in einer for-Schleife oder als Puffer zum Einlesen von Daten) sollen kurze
Namen bekommen. Ein Programmierer, der solche Variablennamen sieht, soll annehmen dürfen, daß ihr
Gültigkeitsbereich einige Zeilen Code nicht überschreitet. Verbreitete Namen solcher Variablen sind i,
j, k, m, n und für Zeichen (character) c und d.
Ebenfalls möglich sind Varianten wie ii oder jj, was sich besonders dann anbietet, wenn man diese später
automatisiert durch andere Namen ersetzen möchte.
Kommentare
Jeglicher Kommentar bezieht sich auf die direkt nachfolgende Zeile(n) und ist
identisch zu dieser (diesen) eingerückt
Konsistenz in der Plazierung von Kommentaren hilft, Probleme bei deren Zuordnung zu vermeiden.
Ebenfalls in Ordnung sind kurze Erklärungen zu einem Ausdruck direkt auf der gleichen Zeile, mit etwas
Abstand zum Ausdruck selbst.
Zu jeder (selbst definierten!) Klasse gehört ein Kommentar, der ihren Zweck erklärt.
Dateien
In jedem Header-File wird genau eine nach außen sichtbare Klasse definiert.
So wenig Deklarationen wie möglich in einem Header-File zu haben, reduziert
Header-Abhängigkeiten.
Die Header-Datei hat den gleichen Namen wie die Klasse und die Endung .h.
Externe "non-member" Funktionen, die dennoch zum Interface einer Klasse gehören,
dürfen im gleichen Header-File deklariert werden.
Dateinamen sollen als "case sensitive" behandelt werden (Groß- und Kleinschreibung macht
einen Unterschied).
Header-Files müssen einen "include" Schutz aufweisen.
Der "include" Schutz bewahrt das Header-File davor, mehrfach eingebunden zu werden.
Beispiel:
|
#ifndef FILE_H
#define FILE_H
...
#endif
|
Der Makroname, der im "include" Schutz eingeführt wird, soll
den gleichen Namen wie die Datei haben (ohne Erweiterung),
gefolgt von dem Suffix "_H".
Header-Files sollen in sich abgeschlossen sein
Wenn ein Header-File eingebunden wird, soll es nicht notwendig sein,
zuvor andere Header-Files einzubinden.
Eine einfache Art, dies zu überprüfen, ist, die Datei als erstes in sich selbst einzubinden.
Beispiel:
|
/* foobar.h */
#include "foobar.h"
#include <stdio.h>
...
|
System-Header sollen mit <> eingebunden werden, Projekt-Header mit "".
Beispiel: Siehe oben.
#include Direktiven stehen am Anfang einer Datei.
Alle #include Direktiven an einem Platz zu haben, hilft, sie schnell zu finden.
Außerdem stehen sie so sicher vor dem Code, der sie benötigt.
Es sollen keine absoluten Pfade oder Dateinamen in #include Direktiven verwendet werden.
Andere Systeme könnten eine andere Verzeichnisstruktur haben.
Beispiel (falsch): #include "C:\Uni\SS05\Prog2\uebung01\aufgabe01.h"
Deklarationen
Parameternamen sollen in der Deklaration einer Funktion mit angegeben werden.
Parameternamen sind nützlich, um zu dokumentieren, wozu der Parameter dient.
Parameternamen sollen in allen Deklarationen und der Definition der Funktion übereinstimmen!
Deklariere geerbte Methoden virtual.
Eine vererbte Methode ist implizit bereits virtual, wenn sie in der Basisklasse als
virtual deklariert war.
Wiederhole das virtual Schlüsselwort in der Deklaration der abgeleiteten Klasse,
um deutlich zu machen, daß diese Methode virtual ist.
Benutze keine globalen Variablen.
Benutze stattdessen Singleton Objekte.
Benutze keine globalen using Deklarationen in Header-Files.
Dies kann Konflikte mit sich bringen, wenn noch andere Header eingebunden werden.
Die Teile einer Klassendefinition sind public,
protected und private, in dieser Reihenfolge.
Dies erleichtert das Lesen der Definition, da sich die meisten Leser am ehesten
für das public Interface interessieren werden.
Deklariere Klassendaten private.
Klassen sollen ihre Informationen einkapseln, und nach außen
nur Zugriff über dafür vorgesehene Funktionen darauf anbieten.
So wird Konsistenz der Daten garantiert.
Die Ausnahme zu dieser Regel ist der Datentyp struct, der nur Variablen beinhaltet.
Statements
Benutze niemals gotos.
Gotos erzeugen Spaghetticode. Niemand will Spaghetticode.
Zu jedem switch Ausdruck gehört ein default Label.
Wenn dieser Fall niemals eintreten kann, sollte dies durch eine Assertion zum
Ausdruck gebracht werden.
Andere Themen
Benutze Preinkrement/Predekrement anstatt von Postinkrement/Postdekrement, wenn
dies sonst keinen Unterschied macht.
Für Postinkrement/Postdekrement muss der Compiler eine zusätzliche temporäre
Variable einführen, um den alten Wert der Variable zu speichern. Obwohl diese für
primitive Datentypen wieder wegoptimiert werden kann, ist es gut, sich diese Notation
anzugewöhnen.
Verlasse dich nicht auf implizite Konvertierung nach bool in Konditionalen.
Beispiel:
|
if (ptr) // falsch
if (ptr != NULL) // ok
|
Generated 2005-04-16 by Coding Standard Generator version 1.12.
Übersetzt von Andi Scharfstein, 19.04.2005