C++ Interop Verständnis Problem
-
Hallo,
Also eigentlich funktioniert der Code den ich weiter unter posten werden. Nur habe ich gewisse Probleme mit dem Verstehen.#include <iostream> using namespace std; using namespace System; using namespace System::Runtime::InteropServices; #pragma unmanaged void foo(char *bla) { cout << "(unmanaged)" << bla << endl; } #pragma managed char* StringConv(String^ myString) { IntPtr ip = Marshal::StringToHGlobalAnsi(myString); return static_cast<char*>(ip.ToPointer()); } int main() { String^ managedString = gcnew String("Test string 1 2 3 "); char* nativeString = StringConv(managedString); foo(nativeString); Marshal::FreeHGlobal((IntPtr)nativeString); }
So jetzt zu meiner Frage(oder besser Fragen ).
Erstens, ist der Speicherplatz für den nativeString richtig wieder freigegeben wie ich es gemacht habe??? In der MSDN Hilfe macht er es ein bisschen anderst. Diese Lösung ist aber für mich nicht wirklich tauglich, da ich mit den IntPtr nicht speicher will.
Zweitens, darf ich StringToHGlobal so verstehen, also ob hier ein new gefolgt von einer memcopy passiert???
Drittens, ist FreeHGlobal sowas ähnliches wie delete??? Und warum wirft mirdelete nativeString
eine exception???
Und noch eine Letzte die ein bisschen weniger mit dem Oben zu tun hat. Warum funktioniert das???
String^ bla = gcnew String("bla bla"); delete bla; Console::WriteLine(bla);
-
paindelux schrieb:
Erstens, ist der Speicherplatz für den nativeString richtig wieder freigegeben wie ich es gemacht habe???
Grundsätzlich ja. Ich würde den Zeiger zwar kapseln und an ein Objekt binden, welches vom Stack geräumt wird, aber das ist eine andere Sache.
paindelux schrieb:
Zweitens, darf ich StringToHGlobal so verstehen, also ob hier ein new gefolgt von einer memcopy passiert???
Genauer: Ein AllocHGlobal gefolgt von einem memcpy gefolgt von einem Nullbyte
paindelux schrieb:
Drittens, ist FreeHGlobal sowas ähnliches wie delete???
Es ist eine Funktion, um mit der WinAPI-Funktion AllocHGlobal angeforderte Bereiche wieder freizugeben.
paindelux schrieb:
Und warum wirft mir
delete nativeString
eine exception???
Weil Du nativeString nicht mit new, sondern mit AllocHGlobal allokiert hast
paindelux schrieb:
Und noch eine Letzte die ein bisschen weniger mit dem Oben zu tun hat. Warum funktioniert das???
String^ bla = gcnew String("bla bla"); delete bla; Console::WriteLine(bla);
Das ist eine Spezialität von C++/CLI: Wenn ein Objekt einen nichttrivialen Destruktor besitzt, implementiert der Compiler automatisch das IDisposable-Interface. Wenn Du jetzt ein mit gcnew angelegtes Objekt mit delete löscht, wird das Objekt ordnungsgemäß disposed. In anderen Sprachen (C#) würde man bla.Dispose() manuell aufrufen oder bla innerhalb eines using-Blocks verwenden.
-
Ok mal danke für die Antworten.
aber ich muss nochmal kurz nachhaken.
Mit Kapseln und an ein Objekt binden meinst du, einen smartPointer draus machen???Und zu der letzten antwort. Es geht bei dem letzten Codestückchen weniger um das delete, als mehr darum, dass ich danach noch auf das Objekt zugreifen kann (Sry war nicht ganz verständlich ausgedrückt).
-
Weil der SPeicher lediglich freigegeben wird. Du kannst in C++ beliebig auf Speicher zugreifen. Du darfst es aber nicht. Mal können da noch die alten Daten drinn stehen, mal steht da was völlig anderes im Speicher.
Sobald Du etwas freigegeben hast, musst Du davon ausgehen, das der Speicherbereich schon für etwas anderes verwendet wird, ein Zugriff auf diesen ähnelt ein wenig Russisch Roulette
-
Hmm... das mag mit Unmanaged-Code so sein... aber bei managed-Code ist es nicht ganz so...
Mit "delete" wird nur das "IDisposable" aufgerufen... und da "String" keines hat macht der Befehl gar nichts...
Der Beispielcode macht folgendes:
void main() { 00000000 push esi 00000001 cmp dword ptr ds:[00952F10h],0 00000008 je 0000000F 0000000a call 790B54C6 0000000f xor esi,esi 00000011 xor esi,esi String^ bla = gcnew String("bla bla"); 00000013 mov eax,dword ptr ds:[022F3064h] 00000019 mov esi,eax delete bla; Console::WriteLine(bla); 0000001b mov ecx,esi 0000001d call 783DC2AC } 00000022 xor eax,eax 00000024 pop esi 00000025 ret
Wie Du siehst macht das "delete bla" gar nichts
Siehe: Destructors and Finalizers in Visual C++
http://msdn2.microsoft.com/en-us/library/ms177197
-
Ok, das wäre dann wohl die korrekte Antwort für die Frage.
Man sollte dann aber erwähnen, das die Anahme sich auf eine spezielle Version der CLR, des Frameworks, der Zielplattform bzw. des Compilersstützt.
Oder kurzum:
Man sollte im Hinterkopf behalten das die Verwendung nach delete bla; Implementierungsdetails ausnutzt die sich ändern könnten.
Es ist zwar unwarscheinlich das System.String plötzlich IDisposible implementiert, aber denkbar wäre das eine Implementierung eines Compilers bei einem delete auf System::String das Objekt als unerreichbar kennzeichnet so das der GC dies bei einem Collect aufräumt.
Nach dem man ja den Assemblercode gesehen hat sieht man ganz deutlich warum man bei der Fragestellung auf das Objekt zugreifen kann (thx) , wer sich aber auf eine solche Implementierung verlässt riskiert IMHO letztendlich undefiniertes verhalten.