|
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.
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.
Das 'delete' liefert nichts zurück.
Tut es sowiso nicht, und es wirft auch nie eine Ausnahme.
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.
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.
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
|
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
|
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.