PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Hilfe: Professioneller Code (?)


Einfachkrank
2002-12-22, 15:21:00
Hi,

ich habe hier mal ein paar Ausschnitte aus Beispielcode des Buches "OpenGL Game Programming", mit denen ich nur bedingt klar komme:
class CMouse
{
public:
CMouse(LPDIRECTINPUT8 pDI, HWND hwnd, bool isExclusive = true);
~CMouse();

bool ButtonDown(int button) { return (m_state.rgbButtons[button] & 0x80) ? true : false; }
bool ButtonUp(int button) { return (m_state.rgbButtons[button] & 0x80) ? false : true; }
int GetWheelMovement() { return m_state.lZ; }
void GetMovement(int &dx, int &dy) { dx = m_state.lX; dy = m_state.lY; }

bool Update();

bool Acquire();
bool Unacquire();

private:
LPDIRECTINPUTDEVICE8 m_pDIDev;
DIMOUSESTATE m_state;
};
Hier ist es die Zeile mit der Funktion ButtonUp() und ButtonDown() in denen jeweils der Anweisungsteil gleich mit dran steht und in denen noch ein Fragezeichen und vorher ist da noch das Zeichen "~". Wenn ich selbst einen Funktionsprototypen schreibe und den Anweisungsteil dranhänge tauchen hunderte von Fehlern auf ???

Das folgende ist ebenfalls aus einer Datei von OpenGL Game Programming mit der man Vektoren behandeln kann. Ich blicke da schon durch, aber wie kann man so was geniales programmieren und wo lernt man das? Gibt`s da Tut`s im Internet oder gute Bücher die ihr kennt?
#include <math.h>


// returns a number ranging from -1.0 to 1.0
#define FRAND (((float)rand()-(float)rand())/RAND_MAX)
#define Clamp(x, min, max) x = (x<min ? min : x<max ? x : max);

#define SQUARE(x) (x)*(x)


struct vector3_t
{
vector3_t(float x, float y, float z) : x(x), y(y), z(z) {}
vector3_t(const vector3_t &v) : x(v.x), y(v.y), z(v.z) {}
vector3_t() : x(0.0f), y(0.0f), z(0.0f) {}

vector3_t& operator=(const vector3_t &rhs)
{
x = rhs.x;
y = rhs.y;
z = rhs.z;
return *this;
}

// vector add
vector3_t operator+(const vector3_t &rhs) const
{
return vector3_t(x + rhs.x, y + rhs.y, z + rhs.z);
}

// vector subtract
vector3_t operator-(const vector3_t &rhs) const
{
return vector3_t(x - rhs.x, y - rhs.y, z - rhs.z);
}

// scalar multiplication
vector3_t operator*(const float scalar) const
{
return vector3_t(x * scalar, y * scalar, z * scalar);
}

// dot product
float operator*(const vector3_t &rhs) const
{
return x * rhs.x + y * rhs.y + z * rhs.z;
}

// cross product
vector3_t operator^(const vector3_t &rhs) const
{
return vector3_t(y * rhs.z - rhs.y * z, rhs.x * z - x * rhs.z, x * rhs.y - rhs.x * y);
}

float& operator[](int index)
{
return v[index];
}

float Length()
{
float length = (float)sqrt(SQUARE(x) + SQUARE(y) + SQUARE(z));
return (length != 0.0f) ? length : 1.0f;
}

vector3_t Normalize()
{
*this = *this * (1.0f/Length());
return *this;
}

union
{
struct
{
float x;
float y;
float z;
};
float v[3];
};
};
Wenn ihr mir da helfen könntet, wäre klasse :)

MFG Einfachkrank

Elemental
2002-12-23, 00:11:36
Hier ist es die Zeile mit der Funktion ButtonUp() und ButtonDown() in denen jeweils der Anweisungsteil gleich mit dran steht und in denen noch ein Fragezeichen und vorher ist da noch das Zeichen "~". Wenn ich selbst einen Funktionsprototypen schreibe und den Anweisungsteil dranhänge tauchen hunderte von Fehlern auf

Das ist doch nur eine Statusabfrage der Buttons.


bool ButtonDown(int button) { return (m_state.rgbButtons[button] & 0x80) ? true : false; }

Bei dieser Schreibweise kannst du keine besonderen Anweisungsblöcke angeben.

Man könnte das auch so formulieren:


bool ButtonDown(int button)
{
if( m_state.rgbButtons[button] & 0x80) )
{
return true;
}

else
{
return false;
}
}



Hoffe das hilft dir weiter.

Gruss
Elemental

Xmas
2002-12-23, 01:35:03
Originally posted by Einfachkrank
Hier ist es die Zeile mit der Funktion ButtonUp() und ButtonDown() in denen jeweils der Anweisungsteil gleich mit dran steht und in denen noch ein Fragezeichen und vorher ist da noch das Zeichen "~". Wenn ich selbst einen Funktionsprototypen schreibe und den Anweisungsteil dranhänge tauchen hunderte von Fehlern auf ???

Die Tilde ~ gefolgt vom Namen der Klasse steht für den Destruktor, das ohne Tilde ist der Konstruktor. Beide haben keinen Rückgabetyp, auch nicht void.

Wie Elemental schon schrieb steht ?: für die konditionale Auswahl eines Wertes.
(a ? b : c) hat den Wert b wenn a "wahr" ist, sonst den Wert c.

Und was für Fehlermeldungen tauchen da auf? Dir ist bewusst, dass du beim Anweisungsteil kein Semikolon hinter '}' setzen darfst (und auch nicht vor dem '{')?


Das folgende ist ebenfalls aus einer Datei von OpenGL Game Programming mit der man Vektoren behandeln kann. Ich blicke da schon durch, aber wie kann man so was geniales programmieren und wo lernt man das? Gibt`s da Tut`s im Internet oder gute Bücher die ihr kennt?
#include <math.h>


// returns a number ranging from -1.0 to 1.0
#define FRAND (((float)rand()-(float)rand())/RAND_MAX)
#define Clamp(x, min, max) x = (x<min ? min : x<max ? x : max);

#define SQUARE(x) (x)*(x)

Naja, so wirklich genial ist das auch nicht, eben eine Standard 3D-Vektor-Klasse. Die Makros hier (bzw. Makros im allgemeinen) sollte man übrigens durch inline-Funktionen ersetzen, das ist wesentlich sicherer und sauberer.

Einfachkrank
2002-12-23, 14:08:00
Hi,

@Xmas
Ja, mir ist diese Schreibweise mit den Semikolons bekannt, den Fehler hab ich schon gefunden, lag wo ganz anders :D
Die Makros meinte ich nicht, sondern in dem Vektor struct, steht dann noch ein union in dem die eigentlichen Werte gespeichert werden, aber vorher kamen die Funktionen mit Operatoren und so. Warum man das genau so anordnen kann war mir irgendwie komisch, aber jetzt ist`s auch klar.

Gut das mit dem "?" und dem ":" ist jetzt klar nur das "~" verstehe ich noch nicht.

MFG Einfachkrank

zeckensack
2002-12-23, 15:46:49
Ist das eigentlich Objective C ???
Wenn man einen C++ Compiler hat, dann kann man das ganze ebensogut als 'class vector3_t' definieren.

Alle anderen Merkmale sind ja schon drin, überladene Konstruktoren, Präambel, Operatoren ...

Der einzige Grund, warum man das auch in C++ als struct deklariert, könnte sein daß man keine Lust hat 'public' und 'private' zu schreiben. In einer struct sind automatisch alle Elemente public.

*grübel*
:|

Xmas
2002-12-23, 16:28:31
Originally posted by Einfachkrank
Gut das mit dem "?" und dem ":" ist jetzt klar nur das "~" verstehe ich noch nicht.

class CMouse
{
public:
CMouse(LPDIRECTINPUT8 pDI, HWND hwnd, bool isExclusive = true);
~CMouse();

[...]
};

CMouse(blabla) ist der Konstruktor der Klasse, ~CMouse() der Destruktor.
Der Konstruktor trägt immer den Namen der Klasse, der Destruktor hat noch ein ~ davor. Beide haben keinen Rückgabewert. Die Tilde hat einzig den Zweck den Destruktor zu markieren.

Der Konstruktor wird automatisch aufgerufen, wenn man eine Instanz der Klasse bildet. Hier werden Member-Variablen sinnvoll initialisiert, evtl. benötigter Speicher allokiert, etc.
Der Destruktor wird automatisch aufgerufen, wenn die Instanz "out of scope" geht, also nicht mehr benötigt wird. Er ist für Aufräumarbeiten zuständig, wie z.B. allokierten Speicher wieder freizugeben.

Einfachkrank
2002-12-25, 16:45:35
Hi,

mhh... und wo nutzt man so was sinnvoll und warum genau? (das mit dem Tilde -Ding) Und woher weiß ich wann ich die allokierten Variablen nicht mehr brauche? Es scheint mir noch nicht ganz so nützlich das Ding :D

zeckensack
2002-12-25, 17:03:27
Originally posted by Einfachkrank
Hi,

mhh... und wo nutzt man so was sinnvoll und warum genau? (das mit dem Tilde -Ding) Und woher weiß ich wann ich die allokierten Variablen nicht mehr brauche? Es scheint mir noch nicht ganz so nützlich das Ding :D :|

#include <malloc.h>
#include <stdio.h>

class Speichermonster
{
public:
Speichermonster();
~Speichermonster();
private:
ubyte* haufen_zeug;
};

Speichermonster::Speichermonster()
{
printf("Ich greif mir mal was ab\n");
haufen_zeug=(ubyte*)malloc(1024);
}

Speichermonster::~Speichermonster()
{
printf("Jetzt geb' ich's wieder her\n");
free(haufen_zeug);
}

int
main()
{
printf("Hallo!\n");
Speichermonster* buh=new Speichermonster();
printf("Das Monster ist da ...\n");
printf("Und jetzt bringen wir es um!\n");
delete buh;
buh=NULL;
printf("Ich muß weg ...\n");
return(0);
}

Automatische Aufräumarbeiten beim anlegen/zerstören von Objekten sind sehr praktisch.

Ausgabe sollte ungefähr so aussehen:
Hallo!
Ich greif mir mal was ab
Das Monster ist da ...
Und jetzt bringen wir es um!
Jetzt geb' ich's wieder her
Ich muß weg ...


Auch für lokale Variablen funktioniert das. Und zwar ganz automatisch.

if (mir_ist_schlecht)
{ //ein Block, ein Block ...
Speichermonster gnarf; //jetzt wird der Konstruktor aufgerufen
printf("Das Monster liegt bei 0x%X!\n",&gnarf);
//wenn ein Block endet, werden seine lokalen Variablen automatisch zerstört
} // ->Destruktor wird jetzt aufgerufen

MeLLe
2002-12-25, 17:04:22
Originally posted by Einfachkrank
Hi,
mhh... und wo nutzt man so was sinnvoll und warum genau? (das mit dem Tilde -Ding) Und woher weiß ich wann ich die allokierten Variablen nicht mehr brauche? Es scheint mir noch nicht ganz so nützlich das Ding :D
Das Tilde-Ding :| rufst Du ja auch nicht explizit auf. Zumindest net im Normalfall. Der Destruktor wird aufgerufen, wenn das zugehörige Objekt den eigenen Gültigkeitsbereich verlässt, d.h. wenn es zerstört wird.
Nehmen wir mal an, Du schreibst eine Klasse xyz. In einem Unterprogramm Deiner wahnsinnig tollen Anwendung instantiierst Du ein Objekt dieser Klasse lokal. Dabei forderst Du per malloc() oder wie auch immer Speicher für die objektinterne Verwendung an (sinnigerweise im Konstruktor, das Ohne-Tilde-Ding halt). Dann machst Du dies und das im Unterprogramm und springst dann zurück ins die Hauptprozedur. Dabei tritt die lokale Objektinstanz der Klasse xyz aus ihrem Gültigkeitsbereich, wird also zerstört. Ohne Destruktor würde der in Konstruktor angeforderte Speicher nun nicht freigegeben werden können, wäre also verloren - ein Speicherleck. Damit Du die Möglichkeit hast, durch ein free() eben diesen Speicher zu befreien, dafür gibt es den Destruktor (der mit der Tilde). Da schreibst Du Dein free()-Zeilchen rein, und Du kannst sicher sein, dass beim Ableben der Objektinstanz der Speicher freigegeben wird.

Einfach, oder? ;)

Edit: Ich geb mir so ne Mühe - und Zecki haut mich doch wech. Naja. Ich habs mit Worten versucht. Sind schwerer zu verfassen als Code ;) :naughty:

Einfachkrank
2002-12-26, 13:06:30
Hi,

@zeckensack
Also in dem zweiten Beispiel scheint es mir irgendwie einen Zusammenhang zu geben, denn am Ende vom Block soll das Speichermonster wieder freigegeben werden ohne dass man was macht! ?
Aber im ersten Beispiel hast du es mit delete quasi selbst entfernt, dann könnte ich doch auch das ~ weglassen, oder nicht? (Ich weiß ich bin stressig :D)

MFG Einfachkrank

Unregistered
2002-12-26, 13:54:24
Hallo!
Ich denke das liegt daran, das bei der ersten variante der pointer auf dem stack landet und das eigentliche objekt auf dem heap. Der pointer würde nach beenden der methode wieder vom stack runterfliegen, der speicher des objekts aber weiter im heap belegt bleiben. Deswegen muß man den durch den aufruf von delete selber wieder freigeben.
Im zweiten fall landet das komplette objekt auf dem stack (ist ja dieses mal nicht als pointer deklariert) und der speicher dafür wird dann beim beenden der methode automatisch wieder freigegeben.
Bin selber c/c++ neuling aber denke das sollte so richtig sein.
Grüße. Jan

Xmas
2002-12-26, 15:32:40
Originally posted by Einfachkrank
Also in dem zweiten Beispiel scheint es mir irgendwie einen Zusammenhang zu geben, denn am Ende vom Block soll das Speichermonster wieder freigegeben werden ohne dass man was macht! ?

Ja genau. Lokale Variablen werden am Ende des Blocks ({}) in dem sie definiert wurden immer automatisch "zerstört", d.h. es wird erst ihr Destruktor aufgerufen (falls es einen gibt), dann der Speicher auf dem Stack freigegeben.


Aber im ersten Beispiel hast du es mit delete quasi selbst entfernt, dann könnte ich doch auch das ~ weglassen, oder nicht? (Ich weiß ich bin stressig :D)
Nein, könntest du nicht.
Der Unreg hat schon in die richtige Richtung gedeutet, ganz korrekt ist es aber nicht.

Im ersten Beispiel passiert folgendes:

Speichermonster* buh=new Speichermonster();

- reserviert 4 Byte für den Pointer buh auf dem Stack
- reserviert 4 Byte (die einzige Member-Variable ubyte* haufen_zeug) für das Objekt auf dem Free Store (=Heap)
- weist dem Pointer buh die Adresse des Objekts zu
- ruft den Konstruktor auf, der durch malloc weitere 1024 Byte auf dem Free Store reserviert und die entsprechende Adresse in haufen_zeug ablegt

delete buh;
- ruft den Destruktor für das Objekt auf, auf das buh zeigt. Innerhalb dieses Destruktors werden die 1024 Byte freigegeben, auf die haufen_zeug zeigt
- gibt die 4 Byte auf dem Free Store frei, die für haufen_zeug benötigt wurden

}
- die schließende Klammer des Blocks schließlich gibt die 4 Byte auf dem Stack frei, die für den Pointer buh benötigt wurden.


Wichtig ist: die 1024 Byte, auf die haufen_zeug zeigt, sind nicht Teil des Objekts, lediglich "in seinem Besitz"! Sie müssen explizit durch den Destruktor wieder freigegeben werden.

zeckensack
2002-12-26, 16:11:17
Originally posted by Xmas
Wichtig ist: die 1024 Byte, auf die haufen_zeug zeigt, sind nicht Teil des Objekts, lediglich "in seinem Besitz"! Sie müssen explizit durch den Destruktor wieder freigegeben werden. [/SIZE]Ganz genau.

Wenn der Destruktor (~Speichermonster) nicht definiert wäre, dann hätte man ein schönes Speicherleck.

Eine der wichtigsten Designideen hinter C++ ist es, den Programmierer vor Flüchtigkeitsfehlern zu bewahren. Dank des Konstruktor/Destruktor-Mechanismus spart man sich jede Menge wiederholten Code, und muß nicht immer alles im Kopf haben. Man kann Klassen einfach nur benutzen, ohne zu wissen wie sie intern funktionieren. Auch wenn man die Klassen selbst geschrieben hat, ist das immer noch ein riesiger Vorteil.

Einfachkrank
2003-01-04, 13:36:45
Hi,

ich war ein paar Tage lang nicht da, deswegen kommt das ein wenig spät. Aber jetzt ist mir ein kleines Licht aufgegangen. Wollte mich nur noch mal für die ausführliche Erklärung bedanken.

MFG Einfachkrank

PS: Hier in dem Forum sind alle viel cooler drauf als in anderen! Mal ein Komplement an die Moderatoren und Mitglieger :D