delete-operator mit falschem Typ aufrufen

Schnelle objektorientierte, kompilierende Programmiersprache.
Antworten
Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

delete-operator mit falschem Typ aufrufen

Beitrag von fat-lobyte » Di Sep 11, 2012 2:58 pm

Hallo!

Ich würde gerne folgendes machen:

Code: Alles auswählen

template <typename T>
struct node
{
	T t;
	node<T>* next;
};

int main()
{
	node<int>* n_ptr = new node<int>();
	int* i_ptr = reinterpret_cast<int*>(n_ptr);
	
	delete i_ptr;
}
Meine Frage ist:
Wird der für node<> allokierte Block beim Aufruf von delete korrekt Freigegeben? Werden die überschüssigen 4 (bzw. 8) Bytes des next-Zeigers ebenfalls gelöscht, oder habe ich da ein Speicherleck?
node<> hat keinen Destruktor der aufgerufen werden müsste.
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: delete-operator mit falschem Typ aufrufen

Beitrag von Xin » Di Sep 11, 2012 3:12 pm

fat-lobyte hat geschrieben:Ich würde gerne folgendes machen:
Warum!?
fat-lobyte hat geschrieben: Meine Frage ist:
Wird der für node<> allokierte Block beim Aufruf von delete korrekt Freigegeben? Werden die überschüssigen 4 (bzw. 8) Bytes des next-Zeigers ebenfalls gelöscht, oder habe ich da ein Speicherleck?
node<> hat keinen Destruktor der aufgerufen werden müsste.
Ich kann die Frage eigentlich nur technisch beantworten: Du übergibst den gleichen Pointer an delete, wie zuvor.
In beiden Fällen wird kein Destruktor aufgerufen, da int keinen haben kann und node<int> keinen hat. Es wird also nur free() mit dem identischen Pointer aufgerufen. Entsprechend würden 8 bzw. 12 Bytes freigegeben.

Aber sauber ist das nicht... bei einem Compiler mit ... etwas merkwürdiger Implementation könnte das Ergebnis willkürlich werden.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: delete-operator mit falschem Typ aufrufen

Beitrag von fat-lobyte » Di Sep 11, 2012 3:34 pm

Xin hat geschrieben:Warum!?
Warum nicht? ;-)
Ich überlege gerade eine etwas "rohere" Datenstruktur zu schreiben und dem User den Zeiger zum Objekt direkt in die Hand zu drücken.

Ich könnte in der struct node<> auch einen Zeiger zum Objekt Speichern, aber beim Hinzufügen des Objekts hätte ich dann zwei Allokiervorgänge (node und objekt) statt einem und beim Zugriff hätte ich eine Dereferenzierung mehr.
Die würde ich mir gerne sparen, auf Kosten von 4/8 bytes, die noch "hinten dranhängen". Die sind mir das wert, solange das mit dem freigeben klargeht.
Xin hat geschrieben:Ich kann die Frage eigentlich nur technisch beantworten: Du übergibst den gleichen Pointer an delete, wie zuvor.
Die Frage ist: ist operator delete() tatsächlich nur eine Weiterleitung von free()? Oder ist das eine überladene oder template-Funktion, die beim Löschen (außer dem Destruktoraufruf) noch das sizeof() irgendwie in Betracht zieht?
Xin hat geschrieben:Aber sauber ist das nicht...
Manchmal ist unsauber viel sauberer als sauber ;-)
Xin hat geschrieben:bei einem Compiler mit ... etwas merkwürdiger Implementation könnte das Ergebnis willkürlich werden.
Ist das gesichert, oder steht vielleicht irgendwo dass zum deallokieren nur die Adresse zählt? Logisch wärs schon, wenn man delete mit free() vergleicht.
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: delete-operator mit falschem Typ aufrufen

Beitrag von fat-lobyte » Di Sep 11, 2012 3:49 pm

Ich glaube übrigens, das müsste funktionieren:
cplusplus.com hat geschrieben:operator delete[] can be called explicitly as a regular function, but in C++, delete is an operator with a very specific behavior: An expression with the delete operator, first calls the appropriate destructors for each element in its array (if needed), and then calls function operator delete[] to release the storage.
Wobei operator delete[] u. a. so deklariert ist:

Code: Alles auswählen

void operator delete[] (void* ptr) throw ();
Das heißt, man braucht tatsächlich nur den Zeiger, wenn man den Destruktoraufruf weglässt. Müsste funktionieren. Sieht noch jemand Probleme?
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: delete-operator mit falschem Typ aufrufen

Beitrag von Kerli » Di Sep 11, 2012 4:03 pm

'operator delete' ist im Standard unter anderen in den folgenden Überladungen definiert:

Code: Alles auswählen

void operator delete(void *ptr);
void operator delete(void *ptr, size_t size);
Man sieht als der Datentyp ist komplett egal :) Hier muss man aufpassen und zwischen den Schlüsselwörtern 'new'/'delete' und den Funktionen 'operator new'/'operator delete' unterscheiden. Bei Verwendung des Schlüsselworts 'new' in einem Ausdruck wird zuerst durch Aufruf der entsprechenden Funktionen (welche man auch für bestimmte Typen überladen kann) Speicher allokiert und anschließend der Konstruktor aufgerufen. Bei 'delete' ist es entsprechend genau umgekehrt.
Wenn du genau wissen möchtest wie viel Speicher 'operator delete' freigeben soll dann kannst du ihn in der zweiten, oben aufgelisteten Signatur überladen und erhältst die Größe des freizugebenden Speicherblocks als Argument.
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: delete-operator mit falschem Typ aufrufen

Beitrag von Xin » Di Sep 11, 2012 4:04 pm

fat-lobyte hat geschrieben:Wobei operator delete[] u. a. so deklariert ist:

Code: Alles auswählen

void operator delete[] (void* ptr) throw ();
Das heißt, man braucht tatsächlich nur den Zeiger, wenn man den Destruktoraufruf weglässt. Müsste funktionieren. Sieht noch jemand Probleme?
Solange Du delete aufrufst, solltest Du operator delete ( void * ptr ) betrachten. ^^

Bei delete [] solltest Du eher noch vorsichtiger sein ;-)

operator delete() kann eine Weiterleitung auf free() sein - muss aber nicht. Dass es etwas anderes als free() - oder die jeweilige Betriebsystemfunktion ist - ist aber unwahrscheinlich.
Das heißt, dass es wahrscheinlich funktioniert.
Wenn etwas wahrscheinlich funktioniert, ist das aber keine Garantie.

Die Weiterleitung auf free() ist nebenher nur dann gegeben, wenn alle beteiligten Typen keinen Destruktor haben.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: delete-operator mit falschem Typ aufrufen

Beitrag von fat-lobyte » Di Sep 11, 2012 4:09 pm

Solange Du delete aufrufst, solltest Du operator delete ( void * ptr ) betrachten. ^^

Bei delete [] solltest Du eher noch vorsichtiger sein ;-)
Stimmt, das habe ich überlesen. Der Text und Deklaration sind aber fast die gleichen.
Xin hat geschrieben:Das heißt, dass es wahrscheinlich funktioniert.
Wenn etwas wahrscheinlich funktioniert, ist das aber keine Garantie.
Habe ich hier nicht eine Art Garantie, wenn operator delete() nur von einem void*-Zeiger abhängt?

Ich glaube ich werde meinen Container erst mal implementieren und dann schauen was passiert. Vielleicht fällt mir für dieses Teilproblem ja noch eine elegantere Lösung ein.
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: delete-operator mit falschem Typ aufrufen

Beitrag von Xin » Di Sep 11, 2012 4:21 pm

fat-lobyte hat geschrieben:
Xin hat geschrieben:Das heißt, dass es wahrscheinlich funktioniert.
Wenn etwas wahrscheinlich funktioniert, ist das aber keine Garantie.
Habe ich hier nicht eine Art Garantie, wenn operator delete() nur von einem void*-Zeiger abhängt?
Dein Problem ist vermutlich nicht operator delete(), sondern delete. operator delete() wird von delete gerufen und was delete macht, das ist dem Compilerhersteller überlassen.

Abgesehen davon weigere ich mich von Garantien zu sprechen, wenn Objekte nach einem reinterpret_cast an Funktionen übergeben werden, die Du nicht selbst geschrieben hast oder die von anderen eventuell überschrieben werden können.

Wir können von hohen Wahrscheinlichkeiten reden.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: delete-operator mit falschem Typ aufrufen

Beitrag von fat-lobyte » Di Sep 11, 2012 7:09 pm

So.

Dann kucken wir uns das mal genauer an. Ich Beziehe mich auf N3337
5.3.5 hat geschrieben:If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will
invoke the destructor (if any) for the object or the elements of the array being deleted. [...]
If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will
call a deallocation function (3.7.4.2). Otherwise, it is unspecified whether the deallocation function will be
called.
Das sagt mir schon mal recht eindeutig was beim delete-Operator passiert: der Destruktor des zu löschenden Typs wird aufgerufen. Anschließend wird die "deallocation function" aufgerufen. Was tut die?
3.7.4.2 hat geschrieben:Each deallocation function shall return void and its first parameter shall be void*.
[...]
If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer
value (4.10), the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid
all pointers referring to any part of the deallocated storage.
Wenn ich das richtig verstanden habe, sind wir da ziemlich auf der sicheren Seite: der delete-Operator ruft tatsächlich nur den Destruktor auf, und löscht dann den Speicher (unabhängig des Typs!).

Übrigens, noch ein Grund warum das funktionieren muss: man hätte sonst überall dort Speicherlecks, wo man abgeleitete Klassen mit Basisklassenzeigern löscht.

Also ich bin zufrieden. Danke für die Anregungen!
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: delete-operator mit falschem Typ aufrufen

Beitrag von Xin » Di Sep 11, 2012 8:53 pm

Das Risiko, dass ich sehe ist, dass eventuell ein Compiler Prüfungen durchführt und dabei auf Ungereimtheiten stößt.
Das ist unwahrscheinlich, aber eben nicht garantiert.

Vom Prinzip her, sehe ich eine hohe Wahrscheinlichkeit, dass es funktioniert, denn technisch ist das, was Du hier beschreibst genau das, was ich im Normalfall erwarte.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Antworten