问题如下:

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.

一个问题:关于类型转换Type Cast(汇编讲解 as 语法)的更多相关文章

  1. c++类型转换Type Cast)

    C风格的强制类型转换(Type Cast)很简单,不管什么类型的转换统统是:TYPE b = (TYPE)a.C++风格的类型转换提供了4种类型转换操作符来应对不同场合的应用. const_cast, ...

  2. C语言ASM汇编内嵌语法

    转载:http://www.cnblogs.com/latifrons/archive/2009/09/17/1568198.html C语言ASM汇编内嵌语法 .3 GCC Inline ASM G ...

  3. JS对象 四舍五入round() round() 方法可把一个数字四舍五入为最接近的整数。 语法: Math.round(x)

    四舍五入round() round() 方法可把一个数字四舍五入为最接近的整数. 语法: Math.round(x) 参数说明: 注意: 1. 返回与 x 最接近的整数. 2. 对于 0.5,该方法将 ...

  4. 打开新窗口(window.open) open() 方法可以查找一个已经存在或者新建的浏览器窗口。 语法: window.open([URL], [窗口名称], [参数字符串])

    打开新窗口(window.open) open() 方法可以查找一个已经存在或者新建的浏览器窗口. 语法: window.open([URL], [窗口名称], [参数字符串]) 参数说明: URL: ...

  5. 类型强转(type cast)

    类型转换有 c 风格的,当然还有 c++风格的.c 风格的转换的格式很简单(TYPEEXPRESSION),但是 c 风格的类型转换有不少的缺点,有的时候用 c 风格的转换是不合适的, 因为它可以在任 ...

  6. MySQL数据类型转换函数CAST与CONVERT的用法

    MySQL 的CAST()和CONVERT()函数可用来获取一个类型的值,并产生另一个类型的值.两者具体的语法如下: 1.CAST(value as type) 就是CAST(xxx AS 类型) 2 ...

  7. mysql 的类型转换函数cast的用法

    CAST(expr   AS   type),   CONVERT(expr,type)   ,   CONVERT(expr   USING   transcoding_name) CAST()   ...

  8. nasm汇编讲解

    一.什么是nasm汇编 nasm使用在windows.linux等系统下的汇编. 二.语法介绍 2.1 nasm 是区分大小写 例如:符号 foo 与 FOO 是两个不同的标识符. 2.2 内存操作数 ...

  9. $.ajax通路RESTful Web Service一个错误:Unsupported Media Type

    最近项目,使用头版jquery ajax访问背景CXF发布时间rest维修,结果遇到了错误"Unsupported Media Type". 公布的服务java代码例如以下: im ...

随机推荐

  1. poj 1084 Brainman(归并排序)

    题目链接:http://poj.org/problem?id=1804 思路分析:序列的逆序数即为交换次数,所以求出该序列的逆序数即可. 根据分治法思想,序列分为两个大小相等的两部分,分别求子序列的逆 ...

  2. FastStone Capture(FSCapture) 注册码 _图形图像_软件教程_脚本之家

    FastStone Capture(FSCapture) 注册码 _图形图像_软件教程_脚本之家 FastStone Capture 注册码 序列号: name/用户名:TEAM JiOO key/注 ...

  3. LNK 2005 error 函数定义也是定义!!

    url=Ccne-rWwUO9tJp5YAPcycUw09__2whgZLpLw2aWVuYuE-fhu46kaVNX4BldWlsxig1tDML47aO_ctD3PcUlGjK"> ...

  4. setInterval定义与调用

    以下是一个倒计时,可以定义定时器为全局变量(ti2),或局部变量(ti). <script type="text/javascript" src="js/jquer ...

  5. randn命令中randn('state')和randn('seed')的不同

     (1)RANDN产生正态分布数的语法: RANDN(N) :产生N× N的矩阵,其元素是按正态分布的数组: RANDN(M,N) and RANDN([M,N]):产生M×N的矩阵: RANDN ...

  6. [Swust OJ 603]--吃饺子大王

      题目链接:http://acm.swust.edu.cn/problem/603/ Time limit(ms): 1000 Memory limit(kb): 65535   同学们还记得我们班 ...

  7. c++參数传递

    定义: 形參:指出如今Sub 和Function过程形參表中的变量名.数组名,该过程在被调用前.没有为它们分配内存.其作用是说明自变量的类型和形态以及在过程中的作用.形參能够是除定长字符串变量之外的合 ...

  8. 【翻译自mos文章】当指定asm disk 为FRA时,11.2.0.3的dbua hang住

    当指定asm disk 为FRA时.11.2.0.3的dbua hang住 来源于: 11.2.0.3 DBUA Hangs While Specifying ASM Disk To FRA (文档 ...

  9. Android蓝牙A2dp profile的使用

    A2dp profile是android支持的一种蓝牙情景模式,一般用于蓝牙立体声耳机,即蓝牙音频的输出 在android的app层中,A2dp的使用并不是很开放,api只提供了非常少的操作接口,连基 ...

  10. WebView使用详解

    一.用WebView处理页面导航 可以加载本地资源也可以加载web资源,区别是我们要加载本地资源要实现在assets文件夹里添加一个.html资源.调取网页的时候可以用loadUrl方法把网址添加进去 ...