Object Pascal Free方法的奇怪现象,涉及到面向对象理论,请高手指点。(200分)

  • 主题发起人 ZXW49362727
  • 开始时间
S

sadj

Unregistered / Unconfirmed
GUEST, unregistred user!
[^]同意楼上PiPi所说,我刚才试过了,的确如此,我的代码如下:
代码:
type
  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    procedure BitBtn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TTest = class
  public
    procedure test;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

{ TTest }

procedure TTest.test;
begin
  Application.MessageBox('sdkjf', '信息', MB_OK+MB_ICONINFORMATION);
end;

procedure TForm1.BitBtn1Click(Sender: TObject);
var
  tt: TTest;
begin
  tt.test
[red]//这里可以成功调用,NND![/red][:D]
end;
看来还有很多是我们不知道的啊![:(]
 
S

sadj

Unregistered / Unconfirmed
GUEST, unregistred user!
通过跟踪刚才的那段代码,我发现,对象在声明的时候就已经有了方法的指针,这个指针
指向地址的地址不会为nil,把他指向nil(tt:=nil)并不改变tt的地址,而不是我们通常
想象的那样,变成nil,那么到底改变了什么呢?
 

教父

Unregistered / Unconfirmed
GUEST, unregistred user!
to ZXW49362727:你以前是不是也用C++啊?我也一直以为静态函数是C++中的概念,Delphi
中没有静态函数的概念,但是刚才翻了书,发现Delphi中也有静态函数的概念,当然,这个
和C++中的还是有一点差别。
to Pipi. :老大,请教你一个问题:你到底有没有什么不懂的东西啊?呵呵,I真是服了U!!

我现在的理解是:每当程序中使用到了一个类时,它的静态函数的入口地址实际上已经是
确实了的,所有的实例都共用这段代码,但却是使用不同的数据区,需要在Create时候进行
内存分配,所以既使没有创建实例,但还是可以访问它的静态方法,却不可以访问它的数据
区。而我上面说的调用Destroy出错,是因为它是虚拟函数,它的入口地址在编译的时候是没有
确定的,只能在运行期根据调用这个虚拟方法的对象实例来决定的,所以会出错。
不知我的理解对不对,还请Pipi.大侠指点。
 
S

sadj

Unregistered / Unconfirmed
GUEST, unregistred user!
Delphi中,即使对象没有创建,只是声明,他的每个方法,甚至虚方法都已经有了确定的
地址了,真是好奇怪阿[:0]
 
J

jrq

Unregistered / Unconfirmed
GUEST, unregistred user!
高手全在这,赶快学习!
 
S

savenight

Unregistered / Unconfirmed
GUEST, unregistred user!
奇怪,怎么都在表扬Pipi.,纵观所有的帖子,可是我先说出的这个观点的呀?[:(]
 
S

sadj

Unregistered / Unconfirmed
GUEST, unregistred user!
faint![:(!]
savenight你说:free是静态方法,在声明的时候就知道它的位置了。
这说明你没有看懂大家在讨论什么问题,或者对对象构造过程不清楚。
 
S

savenight

Unregistered / Unconfirmed
GUEST, unregistred user!
sadj:
我说错了? 说实话,我确实不太懂,不过我的话好像没错吧?
 
S

sadj

Unregistered / Unconfirmed
GUEST, unregistred user!
to:savenight
你说的位置是代码的位置(某某单元,某某行),没错吧?
但问题是一个方法要执行,需要在内存中有一席之地,也就是入口地址。
另外free也不是静态方法,静态方法是用class声明的函数和过程,c++和java中使用static声明。

[?]我不明白的一点就是,为什么delphi在一个对象指针声明的时候就分配方了法的入口地址,这一点
似乎和面向对象的原则冲突,真奇怪啊。
 
S

savenight

Unregistered / Unconfirmed
GUEST, unregistred user!
sadj:
在Delphi里表达 'free是static' 方法的意思就是说,free是不变的,预先就可以知道的,而它调用的是destroy,
destroy是virtual的,是随着对象的变化而变化的。
 
P

Pipi.

Unregistered / Unconfirmed
GUEST, unregistred user!
类的方法在实现上其实是个全局的函数,只是编译器不放你直接调用而已,
比如 类::成员函数(参数1,参数2)
编译器会生成一个普通函数,它的名字规律是: 类_成员函数_参数信息(对象指针,参数1,参数2) (当然不是用下划线隔开的,是用@或者$之类隔开的,vc、bc各有规范)
在c++语言它是不能直接调用 类_成员函数_参数信息 这个函数的
当然你可以通过汇编或者通过指针转换一下来调用
常规下,你只能这样调用: 对象.成员函数(参数1,参数2)
编译器会把它翻译成:类_成员函数_参数信息(对象指针,参数1,参数2)
-----------------------------------------
这个对象指针,指向类的实例,也就是c++的this、delphi的self
实例数据包含了 成员数据、虚函数表(保存虚函数入口) 等,和函数的代码无关
如果对象指针是非法的,它也是一样把这个非法指针传入“类_成员函数_参数信息”
这个函数。这个函数里面如果用这个指针来访问实例数据,那么如果指针非法,
就会出现访问错误,如果这个函数根本没有访问实例数据,就是根本没用
this来访问实例数据(静态成员不算实例数据,它其实广义上也是个全局变量)
那么不管这个指针非法不非法,都不会出错

class C
{
int a;
virtual void f();
void f2();
void f3();
}
实例数据包含了 int a和本类的方法 C::f 的入口,没有包含C::f2、f3的入口
因此 ((C*)非法指针)->f2 可以调用,如果f2不访问a、f,那么不会出错
如果f3也没有访问实例,那么f2访问了this->f3也不会出错,不过如果f3访问了实例数据
那么this指针有问题,就会出错
------------------------
诸如此类
 
P

Pipi.

Unregistered / Unconfirmed
GUEST, unregistred user!
((C*)非法指针)->f() 由于需要从 ((C*)非法指针) 指向的数据里面的虚表得到f的入口
那么会出错
((C*)非法指针)->C::f() 由于 C::f 是客观存在的已知的地址,他不需要再通过虚表
它的调用和f2、f3一样,出错与否看f之否访问实例数据
 
S

sadj

Unregistered / Unconfirmed
GUEST, unregistred user!
标志为virtual只不过是说这个方法可以被覆盖,并且子类的这些方法都是用虚拟方法表vmt
作索引的,比如:
TFather = class
proceudre ReadBook
virtual;
procedure WorkInShop;
procedure Rest
virtual;
end;
TSun = class(TFather)
procedure Rest
override;
procedure WorkInShop;
end;
implementation
procedure TFather.Rest;
begin
if atHome then
ReadBook [red]//ReadBook方法可以被子类扩充[/red]
else WorkInShop
[red]//WorkInShop不能被子类扩充,如果子类有同名的方法,不会被父类认可调用[/red]
end;
procedure TSun.ReadBook;//这个会在上面调用
...
procedure TSun.WorkInShop;//这个不会在上面调用
 
S

savenight

Unregistered / Unconfirmed
GUEST, unregistred user!
sadj: 算了,不合你争了。自己体会吧。

 
S

sadj

Unregistered / Unconfirmed
GUEST, unregistred user!
其实这里不是争论什么东西,如果你的确不清楚一些东西,说错了没关系,大家可以帮助纠正,
但是说错了还要自以为是就很难进步了。在这里上贴子,很难保证句句都对,但是你再没有验证
自己说法正确别人错误之前不要说什么“自己体会”之类的话,依我看,你恐怕还查火候。当然
我这个观点不一定对,我准备随时修正,只要条件具备。
 
S

savenight

Unregistered / Unconfirmed
GUEST, unregistred user!
sadj:你这人怎么这么爱抬杠呢?
我说了,“我确实不太懂”;不过你说说我的话怎么错了?今天就讨论个明白吧!
 
Z

ZXW49362727

Unregistered / Unconfirmed
GUEST, unregistred user!
谢谢大家参与!
我看了各位的发言。
1:Free肯定不是静态方法,这点可以通过VCL源码知道。
2;我认为最有可能的Delphi对Free方法特殊处理过。
如果以上都不是,那我们热爱的VCL岂不是有某些方面并不符合OOP的理念?
 

王寒松

Unregistered / Unconfirmed
GUEST, unregistred user!
类: 描述由任意树木的部分组成的一种类型。 类和记录不同,类包含了方法和函数。 类
有继承特性。

对象: 对象是类的 "动态实例" 对象总是被"动态分配"到堆上。

对象引用: 是指向 一个对象的指针。 在DELPHI中, 使用对象(访问对象的内容)的
唯一办法就是通过对象引用。 对象引用就是我们通常看到的 edit1, edit2
这样的名称。 由于使用的习惯和简便性, 对象引用慢慢的被人们缩写成了
"对象"


类在JAVA和DELPHI中 的表示是: 类是一个指向虚方法表的只读指针表。
类引用是指向方法表的指针。

说来说去。 对象本身是堆上的一些内存数据。 而对象引用(对象名) 是个指针。指到
这些内存数据。 指针是可以设为NIL的。 而对象是要用FREE释放的。
 
D

dcsdcs

Unregistered / Unconfirmed
GUEST, unregistred user!
来自:jrq, 时间:2002-4-19 10:48:00, ID:1053608
高手全在这,赶快学习!

 
S

sadj

Unregistered / Unconfirmed
GUEST, unregistred user!
第二点应该不是,这一点从楼上的例子可以看出,只要方法调用的是已经存在,
PiPi说得对:
“这样实例可能不存在,但是函数是一段固定的代码肯定存在,
实例作为参数传递到这个函数,如果函数并没有引用实例内的数据,那么你即使使用
非法的对象指针也不会出错”(非法的改成nil可能更贴切些)
所以你的结论:我们热爱的VCL不太符合oo的原则和我的结论是一样的。真可悲啊[:(]
 
顶部