Class delphi类中的访问冲突

Class delphi类中的访问冲突,class,delphi,delphi-7,access-violation,Class,Delphi,Delphi 7,Access Violation,我有一门课是这样的: Component = class(TObject) Name: string; CurState: word; States: array of state; constructor Create(nm: string); procedure AddState(ccl: bool; const InB: BufArr; const OutB: BufArr); function Ge

我有一门课是这样的:

Component = class(TObject)  
  Name: string;               
  CurState: word;             
  States: array of state;   
  constructor Create(nm: string);
  procedure AddState(ccl: bool; const InB: BufArr; const OutB: BufArr);
  function GetStateCount(): Integer;
  end;
States
是一个
state
数组,也是一个类声明

 State = class(TObject)    
  InBuf: BufArr;            
  OutBuf: BufArr;           
  Cycle: bool;       
  constructor Create(ccl: bool; const InB: BufArr; const OutB: BufArr);
  end;
每个
组件
可以包含多个
状态
,这就是为什么我有一个
状态
数组的原因

函数应返回组件的状态数。实现如下:

function Component.GetStateCount(): Integer;
begin
  result:=Length(States);
end;
但是,当我在另一个过程中调用此函数时,会出现以下错误:

我似乎想不出这件事的原因。谢谢你的帮助

好的,下面是我调用函数的代码部分:

for i:=0 to nc-1 do
begin
cycle:=false;
len:=cmp[i].GetStateCount;
for j:=0 to len-1 do
   if not cmp[i].States[j].Cycle or cycle then
     continue;
   cycle:=true;
   for k:= 0 to length(cmp[i].States[j].InBuf)-1 do
   begin
     m:=cmp[i].States[j].InBuf[k];
     if m>0 then
     graph[m-1,i]:= graph[m-1,i]+1;
   end;
  end;

读取地址0000000 xx
-注意,引用的地址几乎为零,但略高于该地址-表明您在尝试访问其内部字段(在该函数的调用中)时未创建对象

要测试该假设,请以“防御性编程”的方式重写函数:

如果抛出了自定义异常,那么就是它,您应该调试为什么没有创建组件

components对象的字段变量正在从
Self
指针的值偏移。对于Win32程序,它们通常是
内存对齐的
到4字节(32位)的边界

我们本来是这样的

  • Self+0==>指向VMT(虚拟方法表)的指针
  • Self+4==>
    Component.Name
    //指向
    TStringRec的指针
  • Self+8==>
    组件。CurState
    //使用两个字节,两个字节“填充”
  • Self+12==>
    组件。状态
    //指向dyn数组帧的指针
十六进制0000000 C正好是12,当
(Self=nil)
时为(0+12)

注意:在Delphi7中,一些格式是不同的,例如长字符串没有代码页字段


在问题的代码中,
cmp[i]
有时是
nil
——您必须确定这是否是有效的情况

如果它是有效的,您必须在循环中检查它

for i:=0 to nc-1 do
begin
  if nil = cmp[I] then continue;
  cycle:=false;
  ... 
如果数组中不应有
nil
元素,则必须确定这是如何发生的

for i:=0 to nc-1 do
begin
  if nil = cmp[i] then raise Exception.Create(....);
  cycle:=false;
  ... 
然而,我相信这是另一项研究的重点(可能是一个问题)。在这里,你问为什么在特定的函数中会有AV,我认为你必须检查
Self is nil
假设作为直接原因

另外-在循环体中重复使用
cmp[i]
对我来说并不合适。 我建议您将该值缓存到一个局部变量中,您将为此创建一个局部变量

for i:=0 to nc-1 do
begin
  curr_cmp := cmp[i];
  if nil = curr_cmp then raise Exception.Create(....);
  cycle := false;
  len := curr_cmp.GetStateCount;
  for j:=0 to len-1 do
     if not curr_cmp.States[j].Cycle or cycle then
      ... 
这将使编辑和理解代码更容易。如果明天您将该代码移动到一个单独的过程中,或者决定从不同于
cmp[i]
的源获取对象,您只需要更改一行,而不是搜索所有可能丢失一两行的
cmp[i]
事件。我记得有一次我不得不将索引变量从直接循环更改为更复杂的间接模式,实际上我不得不开始使用两个索引变量,然后延迟复制粘贴
cmp[I]
让我觉得很讨厌


另外,这可能会使代码速度稍微加快。

读取地址0000000 xx
-注意,引用的地址几乎为零,但略高于它-表明您在尝试访问其内部字段(在该函数的调用中)时未创建对象

要测试该假设,请以“防御性编程”的方式重写函数:

如果抛出了自定义异常,那么就是它,您应该调试为什么没有创建组件

components对象的字段变量正在从
Self
指针的值偏移。对于Win32程序,它们通常是
内存对齐的
到4字节(32位)的边界

我们本来是这样的

  • Self+0==>指向VMT(虚拟方法表)的指针
  • Self+4==>
    Component.Name
    //指向
    TStringRec的指针
  • Self+8==>
    组件。CurState
    //使用两个字节,两个字节“填充”
  • Self+12==>
    组件。状态
    //指向dyn数组帧的指针
十六进制0000000 C正好是12,当
(Self=nil)
时为(0+12)

注意:在Delphi7中,一些格式是不同的,例如长字符串没有代码页字段


在问题的代码中,
cmp[i]
有时是
nil
——您必须确定这是否是有效的情况

如果它是有效的,您必须在循环中检查它

for i:=0 to nc-1 do
begin
  if nil = cmp[I] then continue;
  cycle:=false;
  ... 
如果数组中不应有
nil
元素,则必须确定这是如何发生的

for i:=0 to nc-1 do
begin
  if nil = cmp[i] then raise Exception.Create(....);
  cycle:=false;
  ... 
然而,我相信这是另一项研究的重点(可能是一个问题)。在这里,你问为什么在特定的函数中会有AV,我认为你必须检查
Self is nil
假设作为直接原因

另外-在循环体中重复使用
cmp[i]
对我来说并不合适。 我建议您将该值缓存到一个局部变量中,您将为此创建一个局部变量

for i:=0 to nc-1 do
begin
  curr_cmp := cmp[i];
  if nil = curr_cmp then raise Exception.Create(....);
  cycle := false;
  len := curr_cmp.GetStateCount;
  for j:=0 to len-1 do
     if not curr_cmp.States[j].Cycle or cycle then
      ... 
这将使编辑和理解代码更容易。如果明天您将该代码移动到一个单独的过程中,或者决定从不同于
cmp[i]
的源获取对象,您只需要更改一行,而不是搜索所有可能丢失一两行的
cmp[i]
事件。我记得有一次我不得不将索引变量从直接循环更改为更复杂的间接模式,实际上我不得不开始使用两个索引变量,然后延迟复制粘贴
cmp[I]
让我觉得很讨厌

作为奖励