Delphi 无效的指针操作--don';我不知道为什么
我一直在努力解决一个问题,并开发了一个示例应用程序,它显示了(或多或少——错误发生了,但它位于不同的位置)我遇到的问题 这段代码的思想是创建一个对象TGenericList,其中包含一个包含不同类型数据(例如整数、双精度、记录等)的通用对象列表。当其中一个对象发生更改时,它应通知保存该对象的列表 示例程序在运行时,会在第行显示一个EInvalidPointer异常Delphi 无效的指针操作--don';我不知道为什么,delphi,Delphi,我一直在努力解决一个问题,并开发了一个示例应用程序,它显示了(或多或少——错误发生了,但它位于不同的位置)我遇到的问题 这段代码的思想是创建一个对象TGenericList,其中包含一个包含不同类型数据(例如整数、双精度、记录等)的通用对象列表。当其中一个对象发生更改时,它应通知保存该对象的列表 示例程序在运行时,会在第行显示一个EInvalidPointer异常 L.Free; ABase.RegisterObserver(Self); 在应用程序的末尾 在调试器中跟踪时,在TInterf
L.Free;
ABase.RegisterObserver(Self);
在应用程序的末尾
在调试器中跟踪时,在TInterfacedObject例程中引发异常:
procedure TInterfacedObject.BeforeDestruction;
begin
if RefCount <> 0 then
Error(reInvalidPtr);
end;
在TGenericList.AddBase()中,我没有得到异常。还要注意的是,我甚至还没有实现变更通知方法,因此观察者列表从未真正使用过——它只是存在并保存对象引用。在这种情况下,一个
我能想到的唯一一件事是,TList以某种方式释放了观察家,因此当我称L.Free时,它已经是Free'd了。我不认为TList会这么做。帮助文件说TObjectList可以。然后,它似乎永远不会到达列表空闲的那一行,这将释放TBase对象
我编译时没有收到任何警告
我正在运行Delphi-Tokyo(10.2),社区版
program GenericTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Generics.Collections;
type
IObserver = interface
['{DD9243B9-0722-486A-B4BF-0929AB5B6627}']
procedure ObservableChanged(Sender : TObject);
end;
IObservable = interface
['{39EA6448-6636-40F4-B618-740B0BB28127}']
procedure RegisterObserver(Observer : IObserver);
procedure UnregisterObserver(Observer : IObserver);
end;
TBase = class(TInterfacedObject, IObservable)
private
FName : String;
FObservers : TList<IObserver>;
public
constructor Create(AName : String);
destructor Destroy; override;
procedure RegisterObserver(Observer : IObserver);
procedure UnregisterObserver(Observer : IObserver);
property Name : String read FName;
end;
TGenericBase = TBase;
TGenericBase<T> = class(TGenericBase)
private
FData : T;
public
constructor Create(AName : String);
constructor CreateValue(AName : String; AValue : T);
property Data : T read FData write FData;
end;
TGenericList = class(TInterfacedObject, IObserver)
private
FBases : TObjectDictionary<String, TBase>;
public
constructor Create;
destructor Destroy; override;
procedure AddBase(ABase : TBase);
function GetBase<T: TBase>(AName : String) : T;
procedure ObservableChanged(Sender : TObject);
end;
//
// TBase
//
constructor TBase.Create(AName: string);
begin
inherited Create;
FObservers := TList<IObserver>.Create();
FName := AName;
end;
destructor TBase.Destroy;
begin
if (FObservers <> nil) then FObservers.Free;
end;
procedure TBase.RegisterObserver(Observer : IObserver);
begin
if (FObservers <> nil) then FObservers.Add(Observer);
end;
procedure TBase.UnregisterObserver(Observer : IObserver);
begin
if (FObservers <> nil) then FObservers.Remove(Observer);
end;
//
// TGenericBase<T>
//
constructor TGenericBase<T>.Create(AName : String);
begin
inherited Create(AName);
FData := Default(T);
end;
constructor TGenericBase<T>.CreateValue(AName : String; AValue : T);
begin
inherited Create(AName);
FData := AValue;
end;
//
// TGenericList
//
constructor TGenericList.Create;
begin
inherited Create;
FBases := TObjectDictionary<String, TBase>.Create([doOwnsValues], 32);
end;
destructor TGenericList.Destroy;
begin
if (FBases <> nil) then FBases.Free;
inherited Destroy;
end;
procedure TGenericList.AddBase(ABase : TBase);
begin
FBases.Add(ABase.Name, ABase);
// Comment out this line and the error doesn't occur.
ABase.RegisterObserver(Self);
end;
function TGenericList.GetBase<T>(AName : String) : T;
var C : TBase;
begin
if not FBases.TryGetValue(AName, C) then
raise Exception.Create('Couldn''t get base.');
Result := C as T;
end;
procedure TGenericList.ObservableChanged(Sender : TObject);
begin
WriteLn((Sender as TGenericBase).Name);
end;
//
//
//
var C : TGenericBase;
L : TGenericList;
K : Integer;
D : TGenericBase<Double>;
begin
try
L := TGenericList.Create;
try
for K := 0 to 10 do begin
C := TGenericBase<Double>.CreateValue(IntToStr(K), K);
L.AddBase(C);
end;
for K := 0 to 10 do begin
D := L.GetBase<TGenericBase<Double>>(IntToStr(K));
WriteLn(D.Data);
end;
finally
L.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.
程序通用测试;
{$APPTYPE控制台}
{$R*.res}
使用
System.SysUtils、System.Generics.Collections;
类型
IObserver=接口
[{DD9243B9-0722-486A-B4BF-0929AB5B6627}]
观察到的程序已更改(发送方:TObject);
结束;
IObservable=接口
[{39EA6448-6636-40F4-B618-740B0BB28127}]
程序注册观察者(观察者:IObserver);
程序取消注册观察员(观察员:IObserver);
结束;
TBase=类(TInterfacedObject,IObservable)
私有的
FName:字符串;
fobserver:TList;
公众的
构造函数创建(AName:String);
毁灭者毁灭;推翻
程序注册观察者(观察者:IObserver);
程序取消注册观察员(观察员:IObserver);
属性名称:字符串读取FName;
结束;
TGenericBase=TBase;
TGenericBase=class(TGenericBase)
私有的
FData:T;
公众的
构造函数创建(AName:String);
构造函数CreateValue(AName:String;AValue:T);
属性数据:T读FData写FData;
结束;
TGenericList=class(TInterfacedObject,IObserver)
私有的
FBases:TObjectDictionary;
公众的
构造函数创建;
毁灭者毁灭;推翻
程序AddBase(ABase:TBase);
函数GetBase(AName:String):T;
观察到的程序已更改(发送方:TObject);
结束;
//
//t基地
//
构造函数TBase.Create(AName:string);
开始
继承创造;
fobserver:=TList.Create();
FName:=AName;
结束;
析构函数TBase.Destroy;
开始
如果(FObservers nil),则FObservers.Free;
结束;
程序TBase.RegisterObserver(观察者:IObserver);
开始
如果(FObservers nil),则FObservers.Add(Observer);
结束;
程序TBase.UnregisteredObserver(观察者:IObserver);
开始
如果为(FObservers nil),则为FObservers.Remove(Observer);
结束;
//
//TGenericBase
//
构造函数TGenericBase.Create(AName:String);
开始
继承创建(AName);
FData:=默认值(T);
结束;
构造函数TGenericBase.CreateValue(AName:String;AValue:T);
开始
继承创建(AName);
FData:=AValue;
结束;
//
//总目录
//
构造函数TGenericList.Create;
开始
继承创造;
FBases:=TObjectDictionary.Create([DoownsValue],32);
结束;
析构函数TGenericList.Destroy;
开始
如果(FBases nil),则FBases.Free;
继承性破坏;
结束;
程序TGenericList.AddBase(ABase:TBase);
开始
FBases.Add(ABase.Name,ABase);
//注释掉这一行,就不会出现错误。
注册观察者(Self);
结束;
函数TGenericList.GetBase(AName:String):T;
var C:TBase;
开始
如果不是FBases.TryGetValue(AName,C),则
引发异常。创建('无法获取基');
结果:=C为T;
结束;
程序TGenericList.observeChanged(发送方:ToObject);
开始
WriteLn((发送方为TGenericBase).Name);
结束;
//
//
//
var C:TGenericBase;
L:t总目录;
K:整数;
D:TGenericBase;
开始
尝试
L:=TGenericList.Create;
尝试
对于K:=0到10,开始
C:=TGenericBase.CreateValue(IntToStr(K),K);
L.AddBase(C);
结束;
对于K:=0到10,开始
D:=L.GetBase(IntToStr(K));
书面(D.数据);
结束;
最后
L.免费;
结束;
除了
关于E:Exception-do
Writeln(E.ClassName,“:”,E.Message);
结束;
ReadLn;
结束。
如果您释放了L
,并且L
也被用作接口参考,那么您就弄乱了接口的参考计数系统。这会导致你的问题
一般来说:除非您真的知道自己在做什么,否则不要将对象和接口引用混合到同一个对象中。后者不能用简单的答案来解释
简而言之:不要释放也用作接口的对象
当它不再被引用时,接口的自动引用计数最终将释放它。不要干涉那件事。如果在引用计数不为0时释放该项,则会出现“无效指针操作”错误,正如您所发现的那样
有关此方面的更多信息,请参阅Delphi文档:。此文档也可以在帮助文件中找到。如果您释放了
L
,并且L
也被用作接口参考,那么您就弄乱了接口的参考计数系统。这会导致你的问题
一般来说:除非您真的知道自己在做什么,否则不要将对象和接口引用混合到同一个对象中。后者不能用简单的答案来解释
简而言之:不要释放也用作接口的对象
program GenericTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Generics.Collections;
type
IObserver = interface
['{DD9243B9-0722-486A-B4BF-0929AB5B6627}']
procedure ObservableChanged(Sender : TObject);
end;
IObservable = interface
['{39EA6448-6636-40F4-B618-740B0BB28127}']
procedure RegisterObserver(Observer : IObserver);
procedure UnregisterObserver(Observer : IObserver);
end;
TBase = class(TInterfacedObject, IObservable)
private
FName : String;
FObservers : TList<IObserver>;
public
constructor Create(AName : String);
destructor Destroy; override;
procedure RegisterObserver(Observer : IObserver);
procedure UnregisterObserver(Observer : IObserver);
property Name : String read FName;
end;
TGenericBase = TBase;
TGenericBase<T> = class(TGenericBase)
private
FData : T;
public
constructor Create(AName : String);
constructor CreateValue(AName : String; AValue : T);
property Data : T read FData write FData;
end;
TGenericList = class(TInterfacedObject, IObserver)
private
FBases : TObjectDictionary<String, TBase>;
public
constructor Create;
destructor Destroy; override;
procedure AddBase(ABase : TBase);
function GetBase<T: TBase>(AName : String) : T;
procedure ObservableChanged(Sender : TObject);
end;
//
// TBase
//
constructor TBase.Create(AName: string);
begin
inherited Create;
FObservers := TList<IObserver>.Create();
FName := AName;
end;
destructor TBase.Destroy;
begin
if (FObservers <> nil) then FObservers.Free;
end;
procedure TBase.RegisterObserver(Observer : IObserver);
begin
if (FObservers <> nil) then FObservers.Add(Observer);
end;
procedure TBase.UnregisterObserver(Observer : IObserver);
begin
if (FObservers <> nil) then FObservers.Remove(Observer);
end;
//
// TGenericBase<T>
//
constructor TGenericBase<T>.Create(AName : String);
begin
inherited Create(AName);
FData := Default(T);
end;
constructor TGenericBase<T>.CreateValue(AName : String; AValue : T);
begin
inherited Create(AName);
FData := AValue;
end;
//
// TGenericList
//
constructor TGenericList.Create;
begin
inherited Create;
FBases := TObjectDictionary<String, TBase>.Create([doOwnsValues], 32);
end;
destructor TGenericList.Destroy;
begin
if (FBases <> nil) then FBases.Free;
inherited Destroy;
end;
procedure TGenericList.AddBase(ABase : TBase);
begin
FBases.Add(ABase.Name, ABase);
// Comment out this line and the error doesn't occur.
ABase.RegisterObserver(Self);
end;
function TGenericList.GetBase<T>(AName : String) : T;
var C : TBase;
begin
if not FBases.TryGetValue(AName, C) then
raise Exception.Create('Couldn''t get base.');
Result := C as T;
end;
procedure TGenericList.ObservableChanged(Sender : TObject);
begin
WriteLn((Sender as TGenericBase).Name);
end;
//
//
//
var C : TGenericBase;
L : TGenericList;
K : Integer;
D : TGenericBase<Double>;
begin
try
L := TGenericList.Create;
try
for K := 0 to 10 do begin
C := TGenericBase<Double>.CreateValue(IntToStr(K), K);
L.AddBase(C);
end;
for K := 0 to 10 do begin
D := L.GetBase<TGenericBase<Double>>(IntToStr(K));
WriteLn(D.Data);
end;
finally
L.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.