C++ erste Gehversuche (lang)

Hi,

ich versuche mir gerade selber C++ bezubringen, muss aber feststellen, dass mir das nicht ganz gelingt. Als Vorinformation: Ich beherrsche JAVA und C ganz gut.

Ich habe mir eine Klasse (bigInt) geschrieben, die später eine Arithmetik für beliebig (heisst hier sehr gross) lange Zahlen erlauben soll. Leider weisst diese ein paar komische Verhalten auf. Bevor ich die Klasse selber angebe, gebe ich erst einmal das komische Verhalten an:

int main()
{
bigInt r( 339192040 );
printf( „r = %s\n“, r.to_string() );
bigInt b( „339192040“ );
printf( „r = %s\n“, r.to_string() );
printf( „b = %s\n“, b.to_string() );

return 0;
}

Der to_string Ausdruck von r verändert r jedesmal. Schon beim ersten Aufruf ist r nicht mehr das, was es mal war. Was läuft da schief? Ich bin mir sicher, dass meine Implementation daran schuld hat, finde aber den Fehler (mangels Erfahrung nicht). Seid bitte sehr kritisch mit dem Code, ich bin nämlich gerade in der Lernphase.

Vielen Dank,
Franz Prilmeier

Und jetzt die Klasse:

// Klasse bigInt, repraesentiert sehr lange Zahlen, d.h.
// bis zu MAX\_ULONG oder Groesse des Speichers, je nachdem,
// was kleiner ist.

class bigInt
{
public:
 // Konstruktoren
 bigInt();
 bigInt( signed long );
 bigInt( const char \* );
 bigInt( const bigInt& );

 // Destruktor
 ~bigInt();

 // Operatorueberladung
 void operator+( const bigInt & );

 // Ausgabe
 char \* to\_string();

 // Ausnahmen
 class bigIntException{ };
 class bigIntOutOfMemoryException: public bigIntException{ };
 class bigIntDivZeroException: public bigIntException{ };

private:
 // Konstanten, zeigen an, welches Vorzeichen die Zahl haben kann
 // Man koennte auch ein enum verwenden
 static const int BIG\_SIGN\_0 = 0;
 static const int BIG\_SIGN\_PLUS = 1;
 static const int BIG\_SIGN\_MINUS = -1;

 // Vorzeichen der Zahl
 int sign;

 // Anzahl der zur Verfuegung stehenden Dezimalstellen
 unsigned long dgs\_alloc;

 // Anzahl der benutzten Dezimalstellen
 unsigned long dgs\_used;

 // Die Zahl an sich, in jeder Zahl wird nur eine Dezimalstelle gespeichert
 // an der Stelle dp[0] steht die kleinste signifikante Stelle
 unsigned short \*dp;

 unsigned long strlen( const char \*str );
};


// Initialisiere eine Null
inline bigInt::bigInt()
{
 bigInt::dp = new unsigned short[sizeof( unsigned long )];
 if ( bigInt::dp == 0 )
 {
 throw new bigIntOutOfMemoryException();
 }

 \*bigInt::dp = 0;
 bigInt::dgs\_used = 1;
 bigInt::dgs\_alloc = sizeof( unsigned long );
 bigInt::sign = BIG\_SIGN\_0;
}


// Erzeuge ein bigInt aus einer Maschinenzahl
bigInt::bigInt( signed long value )
{
 // Stelle Vorzeichen fest
 if ( value == 0 )
 {
 bigInt::bigInt();
 return;
 }
 else if ( value 0; copy /= 10 ) { bigInt::dgs\_alloc++; }

 bigInt::dp = new unsigned short[bigInt::dgs\_alloc];
 bigInt::dgs\_used = 0;

 // Weise die einzelnen Dezimalstellen zu
 while ( value \> 0 ) {
 bigInt::dp[bigInt::dgs\_used++] = value % 10;
 value /= 10;
 }
}

// Erzeuge ein bigInt aus einer Zeichenkette
bigInt::bigInt( const char \*src )
{
 unsigned long alloclen = bigInt::strlen( src );

 if ( alloclen == 0 )
 {
 bigInt::bigInt();
 return;
 }

 // Ueberlese Leerzeichen
 while ( \*src == ' ' ) { src++; }

 // Moegliches Vorzeichen beachten
 if ( ( \*src == '-' ) || ( \*src == '+' ) )
 {
 bigInt::sign = (\*src == '-') ?
 bigInt::BIG\_SIGN\_MINUS : bigInt::BIG\_SIGN\_PLUS;
 src++;
 }
 else { bigInt::sign = bigInt::BIG\_SIGN\_PLUS; }

 // Kann ja sein, dass da nur ein Vorzeichen war...
 if ( \*src == '\0' )
 {
 bigInt::bigInt();
 return;
 }

 bigInt::dp = new unsigned short[alloclen];
 if ( bigInt::dp == 0 )
 {
 throw new bigIntOutOfMemoryException();
 }

 bigInt::dgs\_used = 0;

 while ( \*src != '\0' )
 {
 // Es sollte schon eine Zahl sein
 if ( \*src '9' )
 {
 break;
 }

 // Es handelt sich ja um ein Charakter, deshalb
 // muss man den kleinsten Charakter, der eine
 // Zahl repraesentiert davon abziehen (streng nach ASCII)

 bigInt::dp[bigInt::dgs\_used++] = \*src - '0';
 src++;
 }

 unsigned short \*new\_dp;

 // Da wollte uns doch jemand hereinlegen ...
 if ( bigInt::dgs\_used == 0 )
 {
 delete [] bigInt::dp;
 bigInt::bigInt();
 }
 else 
 {
 // Reihenfolge umdrehen, die niedrigstwertige Stelle soll
 // ja nach vorne

 new\_dp = new unsigned short[alloclen];
 unsigned long i;

 if ( new\_dp == 0 )
 {
 throw new bigIntOutOfMemoryException();
 }

 for ( i = 0; i b.dgs\_used )
 {
 // this bestimmt die Groesse der neuen Zahl

 new\_dp = new unsigned short[bigInt::dgs\_used + 1];
 if ( new\_dp == 0 ) { throw new bigIntOutOfMemoryException(); }
 bigInt::dgs\_alloc = bigInt::dgs\_used + 1;
 }
 else
 {
 // b bestimmt die Groesse der neuen Zahl

 new\_dp = new unsigned short[b.dgs\_used + 1];
 if ( new\_dp == 0 ) { throw new bigIntOutOfMemoryException(); }
 bigInt::dgs\_alloc = b.dgs\_used + 1;
 }

 unsigned long c\_ld = 0, b\_ld = 0, index = 0;
 unsigned short result, carry = 0;

 // Addiere mit Uebertrag

 while ( b\_ld 10 ) {
 carry = 1;
 result %= 10;
 }
 else { carry = 0; }

 new\_dp[index++] = result;
 }

 unsigned long r\_ld = 0, used = 0;
 unsigned short \*r\_dp = NULL;

 // Ist noch etwas uebrig?
 if ( b\_ld 10 )
 {
 carry = 1;
 }
 else
 {
 carry = 0;
 }

 new\_dp[index++] = result;
 }
 }
 else if ( used != 0 ) {
 while ( r\_ld geklaut
// str muss terminiert sein!

unsigned long bigInt::strlen( const char \*str )
{
 unsigned long result = 0;

 while ( \*str != '\0' )
 {
 result++;
 str++;
 }

 return result;
}

Hi,

ich weiss nicht, inwieweit es schon als Fehler z"ahlt, aber "ublicherweise wird in den Klassenmethoden vor den Klassenfeldern der Klassenname bigInt:: nicht geschrieben, nur bei static definierten klassenglobalen Feldern.

Ansonsten kann ich nur sagen, dass ich solch „intelligentes“ Verhalten von arrays auch schon mal hatte und genauso dumm geguckt hab. Speicheradressen ausgeben lassen, alles ok, aber die Werte waren trotzdem ge"andert nach Neuallokation eines weiteren Vektors ohne Zugriff auf die beobachteten Zahlen.

Evtl. mal mit einem anderen Compiler oder einer neueren (Entwickler-) gcc-Version probieren.

Ciao Lutz

Hi

ich weiss nicht, inwieweit es schon als Fehler z"ahlt, aber
"ublicherweise wird in den Klassenmethoden vor den
Klassenfeldern der Klassenname bigInt:: nicht geschrieben, nur
bei static definierten klassenglobalen Feldern.

Ja, das hat einen Unterschied (den entscheidenden) gebracht. Ich hatte aber in der Originalklasse einen zweiten Konstruktor, der ein bigInt aus einer Zeichenkette erzeugt. Instantiierte ich ein Objekt mit diesem Konstruktor, so gab es das erwähnte Phänomen nicht, obwohl die einzelnen Zahlen in der exakt selben Datenstruktur gespeichert wurde. Sonderbar, nicht?

Ich bin von JAVA eben an das this gewöhnt, um Mehrdeutigkeiten zu vermeiden. Da in C++ ich nicht mit this.Feldname arbeiten kann (der Compiler meckert da schon), habe ich versucht bigInt::Feldname zu nehmen. Der Compiler (mingw von Bloodshed 4.01) hat auch nichts weiter dazu gesagt, also habe ich vermutet, das ist das selbe. Es hat auch bei dem anderen Konstruktor keinen Unterschied gemacht.

Grüsse,
Franz Prilmeier

Hi Franz :smile:

this ist ein Zeiger! Deswegen meckert der Compiler, wenn du this.member schreibst. Es muss this->member heißen :smile: Aber jeder normale C++ Programmierer schreibt das this nur, wenn es absolut nötig ist …

cu Stefan.

Hi,

this ist ein Zeiger! Deswegen meckert der Compiler, wenn du
this.member schreibst. Es muss this->member heißen :smile: Aber
jeder normale C++ Programmierer schreibt das this nur, wenn es
absolut nötig ist …

Ist das dann schlechter Stil, umständlich, oder warum macht man das nicht?

Grüsse,
Franz

Hi Franz :smile:

jeder normale C++ Programmierer schreibt das this nur, wenn es
absolut nötig ist …

Ist das dann schlechter Stil, umständlich, oder warum macht
man das nicht?

Faulheit! Es ist einfach zu viel Tipperei, wenn du vor jede Member-Variable ein „this->“ schreiben würdest, und ungewöhnlich ist es obendrein. Es gibt nämlich Situationen, in denen man das „this->“ wirklich schreiben muss. Hier zum Beispiel:

class CPunkt
{ int x;
 int y;
 ...
 CPunkt (int x=0, int y=0);
};
CPunkt::CPunkt (int x, int y)
{ this-\>x = x;
 this-\>y = y;
}

Die Parameter x und y des Konstruktors überdecken die Member-Variablen x und y der Klasse. Um dennoch Zugriff auf sie zu erlangen, muss der this-Zeiger verwendet werden. Wenn man große Klassen hat und findet plötzlich darin ein „this->member“, dann denkt man normalerweise direkt an Überdeckung, nunja, ich zumindest :smile:

Es hat sich übrigens eingebürgert, Member-Variablen mit einem „m_“ einzuleiten. Dann weiß man später immer, ob man auf Member-Variablen zugreift oder nicht. Nach dieser Konvention würde die Klasse Punkt von oben dann so aussehen:

class CPunkt
{ int m\_x;
 int m\_y;
 ...
};

Dann gibt es auch keine Überdeckungs-Probleme. Einige Programmierer gehen noch einen Schritt weiter, indem sie eine private/protected Member-Variable mit einem Unterstrich abschließen, also etwa so:

class CPunkt
{ int m\_x\_;
 int m\_y\_;
 ...
};

Am Anfang fand ich das auch übertrieben, aber es hilft wirklich, dass man sich in seinen Werken später wieder zurecht findet :smile:

cu Stefan :smile: