关于父类与子类的一点疑问、
(昨天自己去翻译一个C#的代码、那个泛型着实把我膈应了一把、好不容易改写完、也碰到一些小问题、我对于这些概念性的一直很模糊)
- Delphi(Pascal) code
unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;type Aa = class(TObject) public constructor Create; destructor Destroy; function abc:Cardinal; //为了防止编译器优化掉循环 end;type Bb = class(Aa) public ss:TStringList;//用户占用内存方便观察 constructor Create; destructor Destroy; procedure Free; end;type TForm1 = class(TForm) btn1: TButton; btn2: TButton; btn3: TButton; btn4: TButton; btn5: TButton; btn6: TButton; btn7: TButton; btn8: TButton; procedure btn1Click(Sender: TObject); procedure btn2Click(Sender: TObject); procedure btn3Click(Sender: TObject); procedure btn4Click(Sender: TObject); procedure btn5Click(Sender: TObject); procedure btn6Click(Sender: TObject); procedure btn7Click(Sender: TObject); procedure btn8Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end;var Form1: TForm1;implementation{$R *.dfm}{ Aa }function Aa.abc: Cardinal;begin Result:=Random(9999);end;constructor Aa.Create;begin inherited;end;destructor Aa.Destroy;begin inherited;end;{ Bb }constructor Bb.Create;begin inherited; ss:=TStringList.Create;end;destructor Bb.Destroy;begin ss.Free; inherited;end;{如果自己写Free就需要这么写?procedure Bb.Free;begin if Self <> nil then Self.Destroy; Self:=nil;end;}procedure TForm1.btn1Click(Sender: TObject); //Create后不释放var ii,rnd:Cardinal; k:Aa;begin for ii := 0 to 99999 do begin k:=Aa.Create; rnd:=k.abc; //防止编译器把循环给优化掉 end; ShowMessage(IntToStr(rnd) + #13 + '完成了'); //通过观察内存使用量、大概每次占用1.7M内存没有释放end;procedure TForm1.btn2Click(Sender: TObject);var ii,rnd:Cardinal; k:Aa;begin for ii := 0 to 99999 do begin k:=Aa.Create; rnd:=k.abc; k.Destroy; end; ShowMessage(IntToStr(rnd) + #13 + '完成了'); //循环完毕、基本内存使用量无变化(几十K的变化、忽略)end;procedure TForm1.btn3Click(Sender: TObject);var ii,rnd:Cardinal; k:Aa;begin for ii := 0 to 99999 do begin k:=Aa.Create; rnd:=k.abc; k.Free; end; ShowMessage(IntToStr(rnd) + #13 + '完成了'); //循环完毕、基本内存使用量无变化(同调用Destroy方法)end;procedure TForm1.btn4Click(Sender: TObject);var ii,rnd:Cardinal; k:Aa;begin for ii := 0 to 99999 do begin k:=Aa.Create; rnd:=k.abc; FreeAndNil(k); end; ShowMessage(IntToStr(rnd) + #13 + '完成了'); //循环完毕、基本内存使用量无变化(同调用Destroy方法)end;//***********Bb类测试************************************procedure TForm1.btn5Click(Sender: TObject);var ii,rnd:Cardinal; k:Bb;begin for ii := 0 to 99999 do begin k:=Bb.Create; rnd:=k.abc; //防止编译器把循环给优化掉 end; ShowMessage(IntToStr(rnd) + #13 + '完成了'); //通过观察内存使用量、大概每次占用9M内存没有释放end;procedure TForm1.btn6Click(Sender: TObject);var ii,rnd:Cardinal; k:Bb;begin for ii := 0 to 99999 do begin k:=Bb.Create; rnd:=k.abc; //防止编译器把循环给优化掉 k.Destroy; end; ShowMessage(IntToStr(rnd) + #13 + '完成了'); //通过观察内存使用量、使用量基本无变化end;procedure TForm1.btn7Click(Sender: TObject);var ii,rnd:Cardinal; k:Bb;begin for ii := 0 to 99999 do begin k:=Bb.Create; rnd:=k.abc; {防止编译器把循环给优化掉} k.Free; end; ShowMessage(IntToStr(rnd) + #13 + '完成了'); {通过观察内存使用量、每次有8M没有释放}end;procedure TForm1.btn8Click(Sender: TObject);var ii,rnd:Cardinal; k:Bb;begin for ii := 0 to 99999 do begin k:=Bb.Create; rnd:=k.abc; //防止编译器把循环给优化掉 FreeAndNil(k); end; ShowMessage(IntToStr(rnd) + #13 + '完成了'); //通过观察内存使用量、每次有8M没有释放end;procedure TForm1.FormCreate(Sender: TObject);var k:Bb;begin k:=Bb.Create; ShowMessage(IntToStr(k.abc)); { //第一种方法 k.Free; //第二种方法 FreeAndNil(k); //第三种方法 k.Destroy; //第四种方法 //k.Destroy; k:=nil; } ShowMessage(IntToStr(k.abc)); //无论选择四种中的哪一种、依旧可以返回一个数值、这又是为什么呢?end;end.
Aa类和Bb差别只是在创建Bb类的时候、他创建了一个其他的对象实例、
因为Aa/Bb类都没有申明Free方法、那么就应该是调用了继承自TObject的Free方法、
那么当Aa的实例被创建后、其本身的数据结构占用了一定的内存、
如果调用了Free或者Destroy方法、则会把相应的内存给释放、
在Aa中测试、Free和Destroy方法效果几乎是一样的
然而在测试Bb类的时候、Free和Destroy就不一样了、从内存占用的数量来看、应该是Free只释放了自身、却并没有释放类中对象的实例、而FreeAndNil效果基本同Free
所以我有下面的想法、不知道是否正确:
1.我发现Free方法并不调用Destroy方法(我在Destroy里下断点、没断下来;当然可以自己写Free方法来调用Destroy)
2.FreeAndNil(Obj)等价于Obj.Free; Obj:=nil;
3.创建/销毁子类实例、并不会自动的调用父类的构造函数/析构函数、必须在子类的构造/析构函数里用inherited关键字去调用父类的函数、
如果上面第一条我说的是正确的话、那么要实现Free能够释放所有的内存就必须自己在类里面写个Free方法?像这样?
- Delphi(Pascal) code
procedure Bb.Free;begin if Self <> nil then Self.Destroy; Self:=nil;end;
另外还有一个问题、依旧是上面举例的两个类、存在以下代码:
- Delphi(Pascal) code
var k:Bb;begin k:=Bb.Create; ShowMessage(IntToStr(k.abc)); { //第一种方法 k.Free; //第二种方法 FreeAndNil(k); //第三种方法 k.Destroy; //第四种方法 //k.Destroy; k:=nil; } ShowMessage(IntToStr(k.abc)); //无论选择四种中的哪一种、依旧可以返回一个数值、这又是为什么呢?按理来说第二种和第四种方法、已经把变量变成了空指针、为何还能调用函数并获得返回值呢?
delphi中是不可能创建出对象的实例的(其实表述的不对、我想不起来正确应该怎么表示了)、例如:
var
a:TStringList;
无论如何a都不可能是对象的、a永远是个指向对象的指针、这和C++的代码:
TStringList a;
是完全不同的、我觉得delphi里这样的代码应该是等同于C++中
TStringList *a;
那么既然我已经把变量设置成了空指针、上面的竟然还能返回一个值、我恨不能理解、求高手指教、
对于我上面的第三条、如果确实是这样的话、那么就不太理解构造析构函数名强制为Create/Destroy的意义、反正都需要用户显式的去调用(难到这就体现了delphi的严谨?)比如C++申明了一个对象的实例、那么在他生命周期结束时、会自动的调用析构函数、并且子类也会自动的调用父类的析构函数(会不会自动的调用父类的析构函数不确定、别人告诉我是可以的)、
[解决办法]
好长的内容,有点看不过来