zoukankan      html  css  js  c++  java
  • 构造函数 Create 与 析构函数 Destroy

    参考了万一的博客:

    http://www.cnblogs.com/del/archive/2007/12/13/993757.html

    http://www.cnblogs.com/del/archive/2008/01/17/1042904.html

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

    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.

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

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

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

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

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

    针对这个问题,当然有多重解决方案,比如重定义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.

     

     

     

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

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

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

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

     

    2017-05-23 补充:

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

  • 相关阅读:
    笔记44 Hibernate快速入门(一)
    tomcat 启用https协议
    笔记43 Spring Security简介
    笔记43 Spring Web Flow——订购披萨应用详解
    笔记42 Spring Web Flow——Demo(2)
    笔记41 Spring Web Flow——Demo
    Perfect Squares
    Factorial Trailing Zeroes
    Excel Sheet Column Title
    Excel Sheet Column Number
  • 原文地址:https://www.cnblogs.com/del88/p/2325722.html
Copyright © 2011-2022 走看看