Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

WhiteMike

Alter Hase

  • »WhiteMike« ist der Autor dieses Themas

Beiträge: 507

Wohnort: Ulm

Beruf: Schüler

  • Private Nachricht senden

1

29.07.2007, 01:24

Schwieriger Codeabschnitt (aus Irrlicht-Engine)

Hallo an alle,

ich hab nie diesen und ähnliche Abschnitte verstanden. Diesen hier hab ich aus der Irrlicht-Engine und ich wäre sehr dankbar dafür, wenn es mir jemand erklären könnte.

C-/C++-Quelltext

1
2
3
4
// new AND delete DECLARATIONS

void __cdecl operator delete(void *) _THROW0();
void *__cdecl operator new(size_t) _THROW1(std::bad_alloc);
void *__cdecl operator new(size_t, const std::nothrow_t&) _THROW0();


Damit es etwas leichter fällt, schreib ich auf, was ich alles davon verstehe.
Es werden natürlich die delete- und new- Operatoren deklariert, aber ich verstehe nicht, was mit den bereits definierten passiert. Werden wie überladen?
Der Zweck ist der, dass man steuern will, ob eine Ausnahme ausgelöst wird oder nicht (aus Performancegründen?).
Das 'delete' liefert nichts zurück. Was das '__cdecl' heißt, weiß ich trotz nachschlagen leider nicht. Ich hab nur so viel verstanden, dass der Aufrufer (Caller) den Stack dadurch säubern soll, verstehe aber nicht, was es bedeutet. Was bedeutet das '_THROW0()' und wie passt es in die Syntax?
Das erste 'new' ist in etwa das Gleiche. Durch 'bad_alloc' kann man mehr Informationen kriegen, falls das Beanspruchen von Speicher nicht funktioniert soweit ich das richtig verstanden hab.
Das zweite 'new' sieht ja nicht viel anders aus. Das 'const std::nothrow_t&' verstehe ich aber nicht. Nach dem Namen ist es das Gegenteil von 'bad_alloc', aber warum das '&'?

Tut mir Leid, dass es jetzt so viel ist, aber ich würde gerne mir selbst solche Hindernisse freiräumen und sagen können, dass ich solchen Code verstehe. Ich wäre sehr dankbar, wenn ihr mir dabei helfen könntet.

Gruß,
Michael

Mit freundlichen Grüßen,
WhiteMike

grek40

Alter Hase

Beiträge: 1 491

Wohnort: Dresden

  • Private Nachricht senden

2

29.07.2007, 01:58

Also in einigen Sachen kann ich weiterhelfen, aber nicht überall.

zu _THROW0() und _THROW1(std::bad_alloc):
Es gibt die Möglichkeit, dem Compiler explizit mitzuteilen, ob / welche Exceptions von einer Funktion geworfen werden können.

C-/C++-Quelltext

1
2
3
4
5
void Func1() throw();  // diese Funktion wirft keine Exception, sonst hagelt es Fehler


void Func2() throw(std::exception); // diese Funktion kann std::exception werfen, bei allem anderen hagelt es immernoch Fehler (VC++ is da glaub ich bisschen hinter dem Std hinterher, deswegen evtl. auch keine Fehler)


void Func3() throw(...); // diese Funktion kann jede beliebige Exception werfen


Ich vermute mal, dass _THROW0 / 1 irgendwelche defines sind, die sich letztendlich in obiges Bsp. auflösen

Die 2 Varianten von new hängen eben damit zusammen, dass früher new statt einer exception einfach einen Null-Pointer zurückgegeben hat - das ist die Variante der man std::nothrow_t übergibt, genauer hab ich mich nicht mit befasst. Die andere Variante ist die 'normale' und schmeißt bei Fehlschlag eine exception anstatt einen Null-Pointer zurückzugeben

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

3

29.07.2007, 10:31

Re: Schwieriger Codeabschnitt (aus Irrlicht-Engine)

Zitat von »"WhiteMike"«


C-/C++-Quelltext

1
2
3
4
// new AND delete DECLARATIONS

void __cdecl operator delete(void *) _THROW0();
void *__cdecl operator new(size_t) _THROW1(std::bad_alloc);
void *__cdecl operator new(size_t, const std::nothrow_t&) _THROW0();


Damit es etwas leichter fällt, schreib ich auf, was ich alles davon verstehe.
Es werden natürlich die delete- und new- Operatoren deklariert, aber ich verstehe nicht, was mit den bereits definierten passiert. Werden wie überladen?


Nö, die Operatoren new und delete werden hier garnicht überladen/überschrieben oder sonst was. Lediglich die Funktionen operator new/delete werden überschrieben. D.h. beim Aufruf von Operator new, oder delete, werden die überschriebenen Funktionen verwendet und nicht die Standardfunktionen.

Zitat von »"WhiteMike"«


Der Zweck ist der, dass man steuern will, ob eine Ausnahme ausgelöst wird oder nicht (aus Performancegründen?).


Ganz sicher nicht. Der Standard schreibt ziemlich genau vor wie sich Operator new und Operator delete (die Funktionen) zu verhalten haben. Unter Anderem wird dort fest vorgeschrieben das Operator new im Fehlerfall eine Ausnahme vom Typ std::bad_alloc wirft. Hier wird also nichts gesteuert und nicht optimiert was mit Ausnahmen zu tun hat sondern lediglich zwei Versionen vom Operator new überschrieben. Wieso das so ist kann mehrere Gründe haben, z.B. um mit protokolieren zu können wo und wann Speicher reserviert wurde, oder um zu bestimmen wie der Speicher besorgt werden soll.

Zitat von »"WhiteMike"«


Das 'delete' liefert nichts zurück.


Tut es sowiso nicht, und es wirft auch nie eine Ausnahme.

Zitat von »"WhiteMike"«


Was das '__cdecl' heißt, weiß ich trotz nachschlagen leider nicht. Ich hab nur so viel verstanden, dass der Aufrufer (Caller) den Stack dadurch säubern soll, verstehe aber nicht, was es bedeutet.


Das sind sog. "Calling Conventions", dem Compiler wird also mitgeteilt wie eine Funktion aufgerufen werden soll. In diesem Fall sollen die Parameter vom rechts nach links auf den Stack gepusht werden und der Aufrufer löscht den Stack, so das der Stack nach aufrufen der Funktion wieder den Zustand wie vor dem Aufrufen der Funktion hat.

Beispiel:

C-/C++-Quelltext

1
2
3
4
int __cdecl foo( int x, int y )
{
    return 0;
}


Wird zu (in etwa):

Quellcode

1
2
3
4
5
6
7
8
9
10
push ebp
mov ebp, esp
; ... anderer wirrer code um den framepointer aufzusetzen
; ... und Debuginfos
mov eax, 00h ; eigentlicher Funktionscode (return 0)
pop edi
; ...
mov esp, ebp ; alten stack wiederherstellen
pop ebp ; stackframe wiederherstellen
ret


Ein Aufruf

C-/C++-Quelltext

1
int x = foo( 10, 20 );


wird zu

Quellcode

1
2
3
4
5
push 14h ; 2. Parameter (20)
push 0Ah ; 1. Parameter (10)
call 3423434h ; irgendein offset zur Funktion
add esp, 08h ; stack leeren
mov dword ptr [ebp-4], eax ; int x = eax (eax in den stack schieben)


Wie man sieht werden die Parameter von rechts nach links auf den Stack geschoben und der Stack nachher vom Aufrufer wieder gelöscht.

Andere Calling Conventions sind z.B. __stdcall und __fastcall.

Zitat von »"WhiteMike"«


Was bedeutet das '_THROW0()' und wie passt es in die Syntax?


Das hier ist in dem Fall ein Macro. Wahrscheinlich um Warnungen zu unterdrücken die der VC++ bringt, da dieser die Syntax throw( xyz ) hinter Funktionen nicht unterstützt. Was genau das throw() hinter der Funktion macht wurde ja bereits gesagt.

Zitat von »"WhiteMike"«


Das erste 'new' ist in etwa das Gleiche. Durch 'bad_alloc' kann man mehr Informationen kriegen, falls das Beanspruchen von Speicher nicht funktioniert soweit ich das richtig verstanden hab.


Die erste Funktion entspricht der Standardfunktionalität die der new-operator aufruft:

C-/C++-Quelltext

1
int* p = new int; // operator new( std::size_t ) throw( std::bad_alloc ) wird aufgerufen von new


Zitat von »"WhiteMike"«


Das zweite 'new' sieht ja nicht viel anders aus.


In C++ gibts noch eine andere Version von new, nämlich new ( std::nothrow ). Diese Version liefert eine 0-Pointer zurück, falls das Reservieren des Speichers fehlschlug, statt eine Ausnahme zu werfen. Das Verhalten ist von Früher bekannt und wird leider heute immer noch häufig als Standardverhalten angenommen.

So läuft das tatsächlich:

C-/C++-Quelltext

1
2
int* p = new int; // falls hier was schief läuft wird eine Ausnahme geworfen

int* p = new ( std::nothrow ) int; // falls hier was schief läuft ist p einfach ein 0-Pointer


Zitat von »"WhiteMike"«


Das 'const std::nothrow_t&' verstehe ich aber nicht. Nach dem Namen ist es das Gegenteil von 'bad_alloc', aber warum das '&'?


Das const sdt::nothrow_t& kennzeichnet, wie gesagt, die alternative Version von new. Es wird einfach ein Objekt von std::nothrow_t erwartet (dieses existiert bereits: std::nothrow) und wird an die Funktion per konstanter Referenz übergeben, um temporäre Objekte und unnötiges kopieren zu vermeiden. Hat aber garnichts mit der throw( std::bad_alloc ) Klausl zu tun!

Alles was der Code macht ist, drei Funktionen zu deklarieren die die globalen Funktionen zur Speicherallokation und Speicherfreigabe überschreiben. Ob sich die Definition an die Standardvorgaben richtet musst du dort nachsehen.

Hoffe ich konnt dir etwas weiterhelfen beim Verstehen des Codes.
@D13_Dreinig

WhiteMike

Alter Hase

  • »WhiteMike« ist der Autor dieses Themas

Beiträge: 507

Wohnort: Ulm

Beruf: Schüler

  • Private Nachricht senden

4

29.07.2007, 11:27

Wow!
Danke!

Du musst nicht nur hoffen, dass du mir weiterhelfen konntest, weil du das tatsächlich gemacht hast.

Vielen Dank!

Geil!

Mit freundlichen Grüßen,
WhiteMike

Werbeanzeige