use*_*561 0 delphi null access-violation
我有两个类:一个基类和一个派生类基类使用参数定义一个虚方法:
function ToName(MsgIfNil:string=''); virtual;
Run Code Online (Sandbox Code Playgroud)
派生类重新定义了该方法:
function ToName(MsgIfNil:string=''); reintroduce;
Run Code Online (Sandbox Code Playgroud)
这两种方法的实现类似于以下代码:
function TBaseClass.ToName(MsgIfNil:string)
begin
if (Self=nil) then
Result := MsgIfNil
else
Result := Self.SomeProperty;
end;
Run Code Online (Sandbox Code Playgroud)
问题是:
1)如果我没有在派生类中重新引入该方法,但使用常规override关键字,则对此方法的任何调用都会触发访问冲突
2)当我从一个nil的对象调用该方法,并且objet的假定类是TBaseObject时,它崩溃(AV)而不是调用基本虚方法
如果方法中没有定义参数,则调用正确的方法,不带任何AV.即使派生类中的方法被覆盖,它也能很好地工作.
请注意,上述解决方案适用于从TBaseClass派生的任何类的对象
如何定义可以使用Self = nil调用的虚拟方法,可以是虚拟的还是使用参数?
我当然必须加强对内部虚拟方法调用管道工程的理解......
注意:在我的用例中调用nil对象是合法的.它不用于隐藏异常,而是用于报告非链接对象.示例:myEdit.Text:= APerson.Manager.ToName('没有经理定义');
感谢您提供有关正确解决方案的任何建议
使用带有upd5的Delphi 2010
编辑:添加触发AV的更完整的代码示例
TBaseClass = class(TObject)
private
FMyName: string;
public
property MyName: string read FMyName;
function ToName(MsgIfNil:string=''):string; virtual;
end;
TDerivedClass = class(TBaseClass)
private
FSpecialName: string;
public
property SpecialName:string read FSpecialName;
function ToName(MsgIfNil:string=''):string; reintroduce;
end;
TBaseClass.ToName(MsgIfNil:string):string;
begin
if (Self=nil) then
Result := MsgIfNil
else
Result := MyName;
end;
TDerivedClass.ToName(MsgIfNil:string):string;
begin
if (Self=nil) then
Result := MsgIfNil
else
Result := SpecialName;
end;
// Now a sample program
var
aPerson: TBaseClass;
aSpecialist: TDerivedClass;
begin
aPerson := TBaseClass.Create;
aPerson.MyName := 'a person';
aSpecialist := TDerivedClass.Create;
aSpecialist.SpecialName := 'a specialist';
aSpecialist := nil; // For example sake, never do this in my use case :)
// This works here,
// but triggers an AV if ToName is marked as override instead of reintroduce
ShowMessage('Name of the specialist: '+aSpecialist.ToName('No specialist!'));
aPerson := nil;
// This triggers an AV, TBaseClass.ToName is never called
ShowMessage('Name of the person: '+aPerson.ToName('No person!'));
end;
Run Code Online (Sandbox Code Playgroud)
上面的代码可能无法编译,这只是一个更完整的例子
Takeway
我现在明白VMT链接到对象引用,并且无论对象类如何,都不可能在nil对象上调用虚方法(该对象甚至不会查看其声明的类型以获取ToName方法的匹配地址)
我接受了hvd的解决方案,因为它对于必须检查vs nil的方法非常有效(只需添加一个基本方法).
谢谢你的所有答案,
小智 5
调用虚方法nil没有意义:virtual意味着"检查类类型以查看要调用的方法".没有类类型,因此没有方法可以调用.
您可以做的是创建一个调用虚方法的非虚方法:
// TBase
public:
function ToName(MsgIfNil: string = ''): string;
protected:
function ToNameImpl: string; virtual;
// TDerived
protected:
function ToNameImpl: string; override;
function TBase.ToName(MsgIfNil: string): string;
begin
if (Self=nil) then
Result := MsgIfNil
else
Result := ToNameImpl;
end;
function TBase.ToNameImpl: string;
begin
Result := MyName;
end;
function TDerived.ToNameImpl: string;
begin
Result := MyDerivedName;
end;
Run Code Online (Sandbox Code Playgroud)
这确保ToNameImpl了虚拟方法仅在Self不被调用时被调用nil.
编辑:顺便说一下,这正是非TObject.Free虚拟用来调用虚拟的TObject.Destroy.
| 归档时间: |
|
| 查看次数: |
991 次 |
| 最近记录: |