=====================================================================================

 



unit Unit5;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
TForm5 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

/// <summary>
/// 定义一个 人类
/// </summary>
TPerson = class(TObject)
private
Fname: string;
Fage: Integer;
procedure Setage(const Value: Integer);
procedure Setname(const Value: string);
public
/// <summary>
/// 1由于 TObject类的构造方法 不是 虚方法 和 动态方法, 所以不能 overide
/// 所以重定义一个Create
/// 标准写法1
/// </summary>
constructor Create;//overload; 若有多个重载,这里不要忘记加 overload


/// <summary>
/// 重载构造方法, 标准写法2
/// </summary>
//constructor Create(const aName: string; const aAge: Integer); overload;


/// <summary>
/// 1.重写即overide析构方法,由于tobject的析构方法是个虚方法,但是比较特殊,
/// 子类可以选择是否重写(普通方法是不可以选择的)
///
/// 2.这样写是重定义,虽然允许这样玩,但是你要知道父类是个虚方法,尽量不要这样写
/// </summary>
//destructor Destroy;


/// <summary>
/// 标准写法, 重写覆盖父类的虚方法, 加上override 关键词
/// </summary>
destructor Destroy; override;


/// <summary>
/// 重载析构方法, 不要这样玩, 因为你要知道, 我们通常释放对象都是用 MyObj.Free;来
/// 调用Destroy的,而Free是没有参数的, 所以若你这么玩, 那么你必须释放的时候这样写
/// MyObj.Destroy('a123') 且为了安全你还得与Free一致,方法体内释放前判断下对象是否为nil
/// 不如直接用Free来的简单,所以这种方法可以不用.
/// </summary>
//destructor Destroy(a: string); overload;

property name: string read Fname write Setname;
property age: Integer read Fage write Setage;
end;

/// <summary>
/// 定义一个人类的子类 妇女类
/// </summary>
TWoman = class(TPerson)
public
/// <summary>
/// 重定义一个构造方法,测试默认不写inherited Create的时候,是否调用了父类的构造方法
/// 试验证明: inherited Create 不可省略, 不写的时候不调用父类的构造函数,这样才是最
/// 合理的。
/// </summary>
constructor Create;
function makeLove(): string;
end;


var
Form5: TForm5;

implementation

{$R *.dfm}

{ TPerson }

constructor TPerson.Create;
begin
//标准写法
inherited Create;
end;

//constructor TPerson.Create(const aName: string; const aAge: Integer);
//begin
// inherited Create;
// Fname := aName;
// Fage := aAge;
//end;

//destructor TPerson.Destroy(a: string);
//begin
//
//end;

destructor TPerson.Destroy;
begin

end;

procedure TPerson.Setage(const Value: Integer);
begin
Fage := Value;
end;

procedure TPerson.Setname(const Value: string);
begin
Fname := Value;
end;

procedure TForm5.Button1Click(Sender: TObject);
var
pp: TPerson;
mm: TWoman;
begin
pp := TPerson.Create;
mm := TWoman.Create;
try


finally
pp.Free;
mm.Free;
end;

end;

{ TWoman }

constructor TWoman.Create;
begin
//不写这句不调用父类的构造函数,所以还是写上标准 安全。
inherited Create;
end;

function TWoman.makeLove: string;
begin

end;

end.


 

从哲学的角度讲创建一个类的实例是这样的;

创建走正序:父亲.Create ----->> 儿子.Create ----->> 孙子.Create

销毁走逆序:父亲.Destroy <<----- 儿子.Destroy <<----- 孙子.Destroy

即先有父亲,父亲把一些基本通用的成员属性或方法初始化后 才能让儿子继承啊;没有父亲何来儿子呢;

所以一般不要把构造函数Create弄成虚函数。这样做也没意义;构造函数 一个类 重定义一个构造函数,子类的构造函数中可以使用inherit

来调用父类的构造函数,一般构造函数不会定义成虚函数,即不允许 overide ;比如 TObject的构造函数。

析构函数一般都是弄成虚函数,要求子类必须overide(虽然不overide也不报错,但是你要养成良好的习惯,尽量这么做),这样才能保证多态的

使用场景下,调用的是子类的析构函数,即:父类的实例 := 子类的.Create ;父类的实例.Free 依然是调用的

子类.Destroy 确保了;先销毁子类的成员,再销毁父类的成员。

 



unit Unit5;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
TForm5 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

/// <summary>
/// 父亲类
/// </summary>
TFather = class
private
Fname: string;
Flist1: TStringList;
procedure Setname(const Value: string);
procedure Setlist1(const Value: TStringList);
public
//若需要初始化的时候做一些特殊的事的话,那么重定义构造函数
constructor Create;
//若需要销毁的时候做一些特殊事的话,由于祖先类是虚函数,那么父类子类都需要overide到底.
destructor Destroy; override;
property name: string read Fname write Setname;
property list1: TStringList read Flist1 write Setlist1;
end;

/// <summary>
/// 儿子类
/// </summary>
TSon = class(TFather)
private
Fage: Integer;
Flist2: TStringList;
procedure Setage(const Value: Integer);
procedure Setlist2(const Value: TStringList);
public
//若需要初始化的时候做一些特殊的事的话,那么重定义构造函数
constructor Create;
//若需要销毁的时候做一些特殊事的话,由于祖先类是虚函数,那么父类子类都需要overide到底.
destructor Destroy; override;
property age: Integer read Fage write Setage;
property list2: TStringList read Flist2 write Setlist2;
end;

/// <summary>
/// 孙子类
/// </summary>
TGrandson = class(TSon)
private
Fsex: Boolean;
procedure Setsex(const Value: Boolean);
public
//若需要初始化的时候做一些特殊的事的话,那么重定义构造函数
constructor Create;
//没必要了,因为销毁的时候不需要做事
//destructor Destroy; override;
property sex: Boolean read Fsex write Setsex;
//如果不需要销毁的时候做一些事,比如这里我不定义一个TStringList,那么就没有必要去重写父类的Destroy
//property list3: TStringList read Flist3 write Setlist3;
end;


var
Form5: TForm5;

implementation

{$R *.dfm}

procedure TForm5.Button1Click(Sender: TObject);
var
sz: TGrandson;
begin
sz := TGrandson.Create;
try
ShowMessage(sz.name);
ShowMessage(sz.list1.Text);
finally
sz.Free;
end;
end;

{ TFather }

constructor TFather.Create;
begin
inherited Create;
Self.Fname := '小李飞刀';
Self.Flist1 := TStringList.Create;
Self.Flist1.Add('111');
OutputDebugString('父亲');
end;

{ TSon }

constructor TSon.Create;
begin
inherited Create;
Self.Fage := 100;
Self.Flist2 := TStringList.Create;
Self.Flist2.Add('222');
OutputDebugString('儿子');
end;

{ TGrandson }

constructor TGrandson.Create;
begin
inherited Create;
Self.sex := True;
OutputDebugString('孙子');
end;

destructor TFather.Destroy;
begin
Self.Flist1.Free;
inherited;
end;

procedure TFather.Setlist1(const Value: TStringList);
begin
Flist1 := Value;
end;

procedure TFather.Setname(const Value: string);
begin
Fname := Value;
end;

destructor TSon.Destroy;
begin
Self.Flist2.Free;
inherited;
end;

procedure TSon.Setage(const Value: Integer);
begin
Fage := Value;
end;

procedure TSon.Setlist2(const Value: TStringList);
begin
Flist2 := Value;
end;

procedure TGrandson.Setsex(const Value: Boolean);
begin
Fsex := Value;
end;

procedure TForm5.FormCreate(Sender: TObject);
begin
ReportMemoryLeaksOnShutdown := True;
end;

end.


 

构造函数 Create 与 析构函数 Destroy_父类

 

创建孙子类的实例的时候,会逐级先向上一直追溯到TObject.Create,先把继承过来的逐渐初始化一遍,才能轮到自己的。

 

 以下是网上的摘抄,不一定正确,切图:

构造函数 Create 与 析构函数 Destroy_重定义_02

 

构造函数 Create 与 析构函数 Destroy_子类_03

 

我觉得这个人 说的非常好,还是要看基类的,即祖先类。若祖先类 用了 virtual 那么无论是 构造函数 还是 析构函数,若你需要在构造和析构的时候 做一些特殊的事的话,那么你必须overide ,子类继续overide,overide到底。养成良好的编程习惯。

 

接下来我来举个例子来说明为什么,析构函数要能弄成虚方法:

例子1,重定义Destroy然后用Free来释放的话,那么会内存泄露;

构造函数 Create 与 析构函数 Destroy_ide_04

 

构造函数 Create 与 析构函数 Destroy_ide_05

 

针对这个问题,当然有多重解决方案,比如重定义Free方法,或者释放的时候用实例.Destroy ,然后为了安全 大不了 Destroy里 也判断下 Self是否为nil; 但是这些解决方案都是把问题 复杂化的方案了。何必不用overide呢。把父类的Destroy给覆盖掉。不就好了。即使再父类调用Free;由于 是子类创建的实例,那么父类的Destroy也是被子类的覆盖掉了的,那么就能保证TObject.Free;实际上是调用了T人类.Destroy,这样就不会有内存泄露了,这块设计的复杂吧,一般人不深入研究根本不会明白,因为析构的时候,我们并没有纯纯的使用Destroy,而是为了安全使用了Free; 而Free又是定义再父类的。

 



unit Unit5;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
TForm5 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

/// <summary>
/// 人类
/// </summary>
TPerson = class
private
Flist1: TStringList;
procedure Setlist1(const Value: TStringList);
public
constructor Create;
//这里Destroy我选择了重定义,没有覆盖父类TObject.Destroy
//当调用.Free来释放的时候将永远不会执行这个析构函数
destructor Destroy;
property list1: TStringList read Flist1 write Setlist1;
end;

var
Form5: TForm5;

implementation

{$R *.dfm}

{ TPerson }

constructor TPerson.Create;
begin
inherited;
Self.Flist1 := TStringList.Create;
Self.Flist1.Add('111');
end;

destructor TPerson.Destroy;
begin
Self.Flist1.Free;
inherited;
end;

procedure TPerson.Setlist1(const Value: TStringList);
begin
Self.Flist1 := Value;
end;

procedure TForm5.Button1Click(Sender: TObject);
var
pp: TPerson;
begin
pp := TPerson.Create;
try
ShowMessage(pp.list1.Text);
finally
//这里根本就没有调用我们上面声明的Destroy,依然是调用父类的Destroy,
//因为Free是Free是个普通的方法声明父类,所以他依然是调用了父类的Destroy什么都没有做
//所以这里就会有内存泄露
pp.Free;
end;
end;

procedure TForm5.FormCreate(Sender: TObject);
begin
ReportMemoryLeaksOnShutdown := True;
end;

end.


 

 构造函数 Create 与 析构函数 Destroy_构造函数_06

 

 构造函数 Create 与 析构函数 Destroy_构造函数_07

 

 

这篇博客可以说是百忙之中写出来的,由于这块设计的逻辑 会有点绕;我来个结论吧:

1.构造方法,如果子类需要加强,或需要重载,那么就需要重定义;

2.析构方法,如果子类需要加强,那么就需要重写覆盖overide父类的,且析构方法一般不重载。

3.普通方法,如果子类需要加强,那么就需要父类定义成虚方法,然后子类覆盖overide;只有这样才能做到多态的情况下使用。

 

构造函数 Create 与 析构函数 Destroy_构造函数_08

 

 

2017-05-23 补充:

对于继承组件的类 构造方法必须覆盖;

 

构造函数 Create 与 析构函数 Destroy_构造函数_09

 

构造函数 Create 与 析构函数 Destroy_子类_10