To #define or not to #define?

Hallo

In verschiedenen C/C+± Fachbüchern habe ich gelesen, es sollen möglichst keine „magic numbers“ verwendet werden.

Beispiel:

// schlecht?
toggleButton tglBtn[6];
// gut?
#define NUM\_OF\_TGL 6;
toggleButton tglBtn[NUM\_OF\_TGL];
// besser?
const int numOfTgl = 6;
toggleButton tglBtn[tglBtn];
// Am besten?
typdef enum lengthDefs{
 eBtn = 8,
 eTgl = 6,
 etc.
} lengthDefs
toggleButton tglBtn[lengthDefs::eTgl];

Ich arbeite zur Zeit an einer C/C+± Applikation, die Motif als grafische Bibliothek und OCI als Anbindung an eine Oracle- Datenbank verwendet. Die Applikation wurde bereits erfolgreich von HPUX nach Red Hat Linux portiert. Verwendet wird ein GNU- Compiler der 3er- Reihe.
Eines der Probleme ist, dass die Applikation voll von „magic numbers“ ist.

Beispiel: :

// xyz.h
...
toggleButton tglBtn[6];
…
// xyz.C
…
for( i = 0; i 

Was ich schlecht daran finde ist, dass ich im Header und in der C- Datei Anpassungen vornehmen muss um einen "Toggle Button" einzufügen.

Wie kann es besser gemacht werden. Ich dachte an "defines". Es soll aber Probleme geben können, betreffend Symboltabellengrösse des Compilers oder betreffend sinnlosen "Cross Dependencies". Ist da was dran? 
Welche Lösung (define, const, enum) findet Ihr am besten? Eure Meinungen würden mich interessieren.

Vielen Dank.
Gruss, Olli

Wie kann es besser gemacht werden. Ich dachte an „defines“. Es
soll aber Probleme geben können, betreffend
Symboltabellengrösse des Compilers oder betreffend sinnlosen
„Cross Dependencies“. Ist da was dran?

Den Compiler kratzen #defines garnicht, die werden vom Präprozessor schon vernichtet, bevor der Compiler den Code überhaupt zu Gesicht bekommt.

Welche Lösung (define, const, enum) findet Ihr am besten? Eure
Meinungen würden mich interessieren.

PP-defines sind – IMAO – bei reinen Zahlkonstanten (und teilweise bei Strings) noch vertretbar. Schlimm wird es erst, wenn man da Code reinsteckt, weil dann gar wunderliche Sachen passieren können. Es geht allerdings nichts über eine richtige Variable. enums kommen nur mit Integer-Werten klar, und man sollte auch berücksichtigen, dass diese Konstrukte dafür gedacht sind, eine Menge gleichartiger Elemente aufzuzählen. Es ist also nicht unbedingt einsichtig, einfach mal alle numerischen Konstanten in eine gigantische Aufzählung zu stecken.

Normalerweise kann man so ziemlich alles mit const-Werten verbauen ohne dadurch irgendjemanden zu verwirren, diese Lösung wäre also mein Favorit.

Hallo olli,

In verschiedenen C/C+± Fachbüchern habe ich gelesen, es
sollen möglichst keine „magic numbers“ verwendet werden.

Beispiel:

// schlecht?
toggleButton tglBtn[6];

Oh ja, wie Nicos schon geschrieben hat: never ever
in Programmen, deren haltbarkeit > 1 Woche sein soll :wink:

// gut?
#define NUM_OF_TGL 6;
toggleButton tglBtn[NUM_OF_TGL];

Vollkommen ok, so lange Komplexität = 0

#define NAME1 ‚zahl1‘
#define NAME2 ‚zahl2‘

wenn auch als „altmodisch“ eingestuft

// besser?
const int numOfTgl = 6;
toggleButton tglBtn[tglBtn];

Besser oder nicht kann man so nicht leicht
sagen. „Zweckmässig oder nicht“ ist der Punkt.

Sind es modulgültige Konstanten, globale
Parameter oder nur einmal im Funktions-
block vorkommende?

// Am besten?

typdef enum lengthDefs{
eBtn = 8,
eTgl = 6,
etc.
} lengthDefs
toggleButton tglBtn[lengthDefs::eTgl];

Das kann auch Overkill sein und den Code
unlesbar und schwer editierbar machen.
Hier ist wirklich die Zweckmässigkeit
und die Sichtbarkeit (Gültigkeitsbereich)
wichtig.

Ich arbeite zur Zeit an einer C/C+± Applikation, die Motif
als grafische Bibliothek und OCI als Anbindung an eine Oracle-
Datenbank verwendet. Die Applikation wurde bereits erfolgreich
von HPUX nach Red Hat Linux portiert. Verwendet wird ein GNU-
Compiler der 3er- Reihe. Eines der Probleme ist, dass die
Applikation voll von „magic numbers“ ist.

Beispiel: :

// xyz.h

toggleButton tglBtn[6];

// xyz.C

for( i = 0; i
Was ich schlecht daran finde ist, dass ich im Header und in
der C- Datei Anpassungen vornehmen muss um einen „Toggle
Button“ einzufügen.

Du musst Dir demnach einen Mechanismus ausdenken,
mit dem Du schmerzlos eine Erweiterung Deiner
Buttonanzahl machen kannst. Hast Du dieses
Konzept im Kopf, realisierst Du es so, dass
ann einer definierten Stelle im Codesystem
eine Änderung gemacht werden muss bzw.
das Programm liest ein Parameterfile ein
und erzeugt die Größe dynamisch nach
den Angaben in dem File.

Wie kann es besser gemacht werden. Ich dachte an „defines“. Es
soll aber Probleme geben können, betreffend
Symboltabellengrösse des Compilers oder betreffend sinnlosen
„Cross Dependencies“. Ist da was dran?

Wie Nicos schon sagt, diese Probleme existieren nicht.

Welche Lösung (define, const, enum) findet Ihr am besten? Eure
Meinungen würden mich interessieren.

Ich würde sowas falls es geht dynamisch machen
und den Funktionen, die die Buttons erzeugen,
deren Eigenschaften „von oben“ mitgeben, um
dann am Anfang irgendwo

 ...
 toggleButton ptglBtn = new toggleButton [tBi.nButtons];
 ...

zu machen. Aber das kommt darauf an. Auf den ganzen Rest :wink:

Grüße

CMБ

Danke Nicos und Semjon für die Antwort.

Eines noch:
Wäre es sinvoll, Konstanten die sich auf eine Klasse beziehen, als Klassenkonstanten zu definieren? Also sowas in der Art.

class bsp{
 public:
 const int mTheIntConst,
 bsp() : mTheIntConst( 256 ){}
};

Dazu noch die Frage, wie const char Arrays in einer Klasse initialisiert werden können. Oder ist das gar nicht möglich? Muss ich einen char- Pointer definieren, im Konstruktor new und im Destruktor delete machen? Im char- Array soll z.B. „Hallo Welt.\n“ (was auch sonst :wink: stehen.

Dank und Gruss
Olli

Wäre es sinvoll, Konstanten die sich auf eine Klasse beziehen,
als Klassenkonstanten zu definieren?

Ist sinnvoll. Kann man je nach Bedarf und Möglichkeit auch static machen, so dass man nach deinem Beispiel einfach auf ein für alle Instanzen gültiges bsp::mTheIntConst zugreifen kann (wobei man dann allerdings die Kennzeichnung als Member weglassen sollte).

Dazu noch die Frage, wie const char Arrays in einer Klasse
initialisiert werden können. Oder ist das gar nicht möglich?
Muss ich einen char- Pointer definieren, im Konstruktor new
und im Destruktor delete machen? Im char- Array soll z.B.
„Hallo Welt.\n“ (was auch sonst :wink: stehen.

Du musst wohl den Weg über Zeiger und dynamische Allokation gehen oder jeder Instanz gleich einen hinreichend großen Puffer mitgeben. Aber:

std::string r0xx0rz teh big one!!!111

Mal ernsthaft: wenn du nicht irgendwelche eigenen, hocheffizienten Algorithmen auf Zeichenketten loslassen willst solltest du ernsthaft überlegen, die in C++ standardisierte „string“-Klasse zu benutzen (definiert im Header string). Das erspart dir die ganze Speicherverwaltung, Initialisierung und sonstigen Spaß bei dem sich 99,999% aller Programmierer früher oder später verlaufen.

Hallo Olli

Beispiel: :

// xyz.h

toggleButton tglBtn[6];

// xyz.C

for( i = 0; i

Was ich schlecht daran finde ist, dass ich im Header und in
der C- Datei Anpassungen vornehmen muss um einen „Toggle
Button“ einzufügen.

Es fragt sich jetzt wie diese 6 toggleButton angesprochen werden sollen, bzw was übersichtlicher ist.

entweder benötigst du nur die Information, dass 6 verwendet werden:

Beispiel: :

// xyz.h

#define TOTAL_TGL_BTN 6


toggleButton tglBtn[TOTAL_TGL_BTN];

// xyz.C

for( i = 0; i TOTAL_TGL_BTN; i ++ )
{
new toggleButton( theWidget, this, tgloBtn[i], … );

Wenn aber jedes Elemt eine bestimmte Bedeutung hat und auch direkt angesprochen werden soll:

Beispiel: :

// xyz.h

enum {BTN_T0, BTN_T1, BTN_T2, BTN_T3, BTN_T4, BTN_T5, MAX_BTN} eTGL_BTN;


toggleButton tglBtn[MAX_BTN];

// xyz.C

for( i = 0; i MAX_BTN; i ++ )
{
new toggleButton( theWidget, this, tgloBtn[i], … );

Hier kannst du dann, bei geiegnetem Namen dirket auf eine BTN zugreifen:
tgloBtn[BTN_T2]…

MfG Peter(TOO)

Thanx @all
Meinen Dank an Peter, Semjon und Nicos für die interessanten Anregungen. Damit habe ich alles um ( hoffentlich :wink: etwas schlaues daraus machen zu können.

Grüsse, Olli