Zitat von
knochen:
Das würde dann wohl der Compiler verbieten,
Genau. Und jetzt stell dir mal vor das Free würde standardmäßig den Objektzeiger auf NIL setzen.
Schon könnte man hier das Objekt nicht mehr "ordentlich" freigeben.
Egal ob man es über einen Compilerschalter an- oder ausstellen kann ... sowas wird ja global gesetzt und ein lokaler Compilerschalter ist Schwachsinn, denn da kann man auch gleich FreeAndNil verwenden, welches ich dann übersichtlicher/verständlicher finde.
PS: C# ist was anderes, denn dort arbeitet auch ein GarbageCollector und es gibt insgesamt einen anderes Speichermanagement.
Dort gibt es quasi keine Objekte, sondern eigentlich nur sowas wie Interfaces und der Speichermanager hat die volle Kontrolle über die ganzen Zeiger, bzw. er muß sogar die Kontrolle haben, sonst geht sowas garnicht.
Zitat von
generic:
Prüft nicht Assigned intern nur auf NIL?
Genau.
Wie gesagt, wenn man auf die interne Struktur schaut, dann kann das nix werden, denn
- entweder liegt an der Stelle irgendwas, welches zufällig so aussueht, wie das auf welches man prüfen will
- oder es liegt inzwischen ein anderes Objekt dort. (siehe nachfolgender Code)
Bsp:
Delphi-Quellcode:
var ObjA, ObjB: TMyObject;
ObjA := TMyObject.Create;
ObjA.Free;
ObjB := TMyObject.Create;
if ObjektAssigned(ObjA) then
// wenn jetzt zufällig der Speicher des Objekts B an der selben Stelle liegt,
// wo vorher das Objekt A lag, dann würde ObjektAssigned an dieser Stelle
// natürlich ein Objekt vorfinden (und zwar das aus ObjB) und würde demnach
// korrekter Weise TRUE liefern, obwohl in ObjA eigentlich kein Objekt mehr drin ist.
Delphi-Quellcode:
program Project3;
uses
Windows;
type
TString20 = String[20];
TStrObject = class
private
FStr: TString20;
public
constructor Create(const S: TString20);
property Str: TString20 read FStr;
End;
constructor TStrObject.Create(const S: TString20);
begin
FillChar(FStr, SizeOf(FStr), 0);
FStr := S;
end;
var
ObjA, ObjB: TStrObject;
begin
ObjA := TStrObject.Create('Ich bin A.');
ObjA.Free;
ObjB := TStrObject.Create('Ich aber bin B?');
MessageBoxA(0, PAnsiChar(@ObjA.Str[1]), nil, 0);
if ObjA = ObjB then ;
end.
ObjA sagt:
Ich aber bin B?
Delphi-Quellcode:
program Project3;
uses
Windows;
type
TString20 = String[20];
TStrObject = class
private
FStr: TString20;
public
constructor Create(const S: TString20);
property Str: TString20 read FStr;
End;
constructor TStrObject.Create(const S: TString20);
begin
FillChar(FStr, SizeOf(FStr), 0); // nur wegen der #0 hinter dem String
FStr := S;
end;
var
ObjA, ObjB: TStrObject;
begin
ObjA := TStrObject.Create('Ich bin A.');
FreeAndNil(ObjA);
ObjB := TStrObject.Create('Ich bin B.');
if Assigned(ObjA) then
MessageBoxA(0, PAnsiChar(@ObjA.Str[1]), nil, 0)
else
MessageBoxA(0, 'ObjA existiert nicht.', nil, 0);
if ObjA = ObjB then ; // für's Debugging
end.
ObjA sagt:
ObjA existiert nicht.
Delphi-Quellcode:
program Project3;
uses
Windows, SysUtils;
type
TString20 =
String[20];
TStrObject =
class
private
FStr: TString20;
public
constructor Create(
const S: TString20);
property Str: TString20
read FStr;
End;
constructor TStrObject.Create(
const S: TString20);
begin
FillChar(FStr, SizeOf(FStr), 0);
// nur wegen der #0 hinter dem String
FStr := S;
end;
function IsValidObject(aObject: TObject): Boolean;
type
PVmt = ^TVmt;
TVmt =
packed record
SelfPtr: TClass;
ignored:
array [0..-(4 + vmtSelfPtr) - 1]
of Byte;
end;
var
VMT: PVmt;
begin
Result := False;
if IsBadReadPtr(aObject, 4)
then
Exit;
VMT := PVmt(aObject.ClassType);
Dec(
VMT);
if IsBadReadPtr(
VMT, 4)
then
Exit;
if aObject.ClassType =
VMT.SelfPtr
then
Result := True;
end;
var
ObjA, ObjB: TStrObject;
begin
ObjA := TStrObject.Create('
Ich bin A.');
ObjA.Free;
ObjB := TStrObject.Create('
Ha, ich bin B.');
if IsValidObject(ObjA)
then
MessageBoxA(0, PAnsiChar(@ObjA.Str[1]),
nil, 0)
else
MessageBoxA(0, '
ObjA existiert nicht.',
nil, 0);
if ObjA = ObjB
then ;
// für's Debugging
end.
Delphi-Quellcode:
program Project3;
uses
Windows, SysUtils;
type
TString30 =
String[30];
TStrObject =
class
private
FStr: TString30;
public
constructor Create(
const S: TString30);
property Str: TString30
read FStr;
End;
constructor TStrObject.Create(
const S: TString30);
begin
FillChar(FStr, SizeOf(FStr), 0);
// nur wegen der #0 hinter dem String
FStr := S;
end;
function IsValidObject(aObject: TObject): Boolean;
type
PVmt = ^TVmt;
TVmt =
packed record
SelfPtr: TClass;
ignored:
array [0..-(4 + vmtSelfPtr) - 1]
of Byte;
end;
var
VMT: PVmt;
begin
Result := False;
if IsBadReadPtr(aObject, 4)
then
Exit;
VMT := PVmt(aObject.ClassType);
Dec(
VMT);
if IsBadReadPtr(
VMT, 4)
then
Exit;
if aObject.ClassType =
VMT.SelfPtr
then
Result := True;
end;
var
ObjA: TStrObject;
begin
ObjA := TStrObject.Create('
Huch, was ist denn das hier?');
ObjA.Free;
if IsValidObject(ObjA)
then
MessageBoxA(0, PAnsiChar(@ObjA.Str[1]),
nil, 0)
else
MessageBoxA(0, '
ObjA existiert nicht.',
nil, 0);
if ObjA =
nil then ;
// für's Debugging
end.
ObjA sagt:
Huch, was ist denn das hier?
Delphi-Quellcode:
program Project3;
uses
Windows, SysUtils;
type
TString20 = String[20];
TStrObject = class
private
FStr: TString20;
public
constructor Create(const S: TString20);
property Str: TString20 read FStr;
End;
constructor TStrObject.Create(const S: TString20);
begin
FillChar(FStr, SizeOf(FStr), 0); // nur wegen der #0 hinter dem String
FStr := S;
end;
var
ObjA, ObjB: TStrObject;
begin
ObjA := TStrObject.Create('Ich bin A.');
FreeAndNil(ObjA);
ObjB := TStrObject.Create('Ich bin B.');
if Assigned(ObjA) then
MessageBoxA(0, PAnsiChar(@ObjA.Str[1]), nil, 0)
else
MessageBoxA(0, 'Ich existiere nicht.', nil, 0);
if ObjA = ObjB then ; // für's Debugging
end.
ObjA sagt:
Ich existiere nicht.
Zitat von
knochen:
Schlimm genung, denn das ist ja das Problem...
Ich sehe da kein Problem.
In Delphi liegt die Kontrole über den Speicher beim Programmierer (abgrsehn einige bestimmter Typen, wie Strings und dyn. Arrays) und via .Free und FreeAndNil hat er die Kontrolle darüber was gemacht werden soll ...... er
muß es nur ordentlich nutzen.