• 一个问题:关于类型转换Type Cast(汇编讲解 as 语法)


    问题如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
      TBase = class
     
      end;
     
      TChild = class(TBase)
      public
        F1:Integer;
        procedure Say;
      end;
     
    implementation
     
    {$R *.dfm}
     
    procedure TForm3.FormCreate(Sender: TObject);
    var
      A:TBase;
      B:TChild;
    begin
      A:=TBase.Create;
      try
        B:=A as TChild;//编译成功,但是运行报错
        B.Say;
      finally
        A.Free;
      end;
    end;
     
    { TChild }
     
    procedure TChild.Say;
    begin
      ShowMessage('%D',[F1]);
    end;

    首先,为什么会报错?编译成功代表语法没有问题.
    我们看看As 是怎么实现的?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    function _AsClass(Child: TObject; Parent: TClass): TObject;
    {$IFDEF PUREPASCAL}
    begin
      Result := Child;
      if not (Child is Parent) then
        Error(reInvalidCast);   // loses return address
    end;
    {$ELSE}
    asm
            { ->    EAX     left operand (class)    }
            {       EDX VMT of right operand        }
            { <-    EAX      if left is derived from right, else runtime error      }
            TEST    EAX,EAX//如果对象是Nil就退出
            JE      @@exit
            MOV     ECX,EAX
    @@loop:
            MOV     ECX,[ECX]//获取自己的类型
            CMP     ECX,EDX//与要转换的类型进行比较
            JE      @@exit//一样就退出
            MOV     ECX,[ECX].vmtParent//不一样就取父类来比较
            TEST    ECX,ECX//判断父类是否为空.
            JNE     @@loop
     
            {       do runtime error        }
            MOV     AL,reInvalidCast//如果都不能匹配就报错
            JMP     Error
     
    @@exit:
    end;
    {$ENDIF}

    可以看到As会对类型进行一些列要求,而且是必须的.
    如果我们把代码改一下用强制类型转换看看:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    procedure TForm3.FormCreate(Sender: TObject);
    var
      A:TBase;
      B:TChild;
    begin
      A:=TBase.Create;
      try
        B:=TChild(A);//没有错误
        B.Say;//但是 弹出来的数据非0
      finally
        A.Free;
      end;
    end;

    现在运行不会错了,但是弹出来的数据却不是0,为什么?成员变量会初始化的,那么如果创建一个TChild对象的话,
    F1就应该是0,不管我们写没有写F1:=0;
    要解释这个就需要用到我们之前的知识了.
    正常情况下,A的内存:

    偏移 0-3 4-7
    内容 TBase的地址 $00000000

    正常情况下,B的内存:

    偏移 0-3 4-7 8-B
    内容 TChild地址 $00000000 F1变量的值

    也就是F1位于对象地址后面8个字节处.
    那么上面的代码中,我们TChild.Say; 会用到F1,而我们把A当成TChild的实例了,是吧?那么去找F1,它就会跑到A+8的地方去,
    但是我们看见实际A+8的地方不属于A管,所以这4个字节是未知的,没有被初始化成0,所以报出来也不太可能是0了.
    谁叫A是下黑手去抢的内存.

    从上面我们可以看到直接强制类型转换速度效率会快一些,所以我们在明白这样转换不会出问题就多用这样转换,但是这中间缺少检验的过程,所以有时候转换后结果可能会错误.
    所以我们需要注意了:

    1
    2
    If Sender Is TButton then
        TButton(Sender).XXX;//别再用Sender As TButton了,都判断过了

    好,今天就唠叨到这里,我是DH.

  • 相关阅读:
    django template extends
    python sys.path的用法
    django form 显示
    django form 验证
    WingIDE 下载,介绍和配置
    python startswith
    django reverse()
    python 动态创建类
    django form 定义
    如何学习python
  • 原文地址:https://www.cnblogs.com/findumars/p/5185254.html
Copyright © 2020-2023  润新知