• 虚方法、抽象方法、抽象类、重定义、覆盖重写------我自己


    参考万一的博客:

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

    http://www.cnblogs.com/del/archive/2007/12/05/983657.html

    http://www.cnblogs.com/del/archive/2007/12/05/983684.html

    http://www.cnblogs.com/del/archive/2008/01/16/1041168.html

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

    http://www.cnblogs.com/del/archive/2008/01/15/1039998.html

    1.虚方法 包括 virtual 和dynamic ,虚方法 必须再父类实现(当然你可以弄个空方法体),子类可以选择是否覆盖,孙类也可以再次覆盖。

    2.纯虚方法 也叫抽象方法,父类必须只定义,不实现,子类必须覆盖overide,没有选择性。孙类也可以再次覆盖。

    3.抽象类 与 普通类 的唯一区别是 抽象类中 可以包含 纯虚方法(也叫抽象方法),纯虚方法 必须只定义不能实现,只能有子类来实现。

    4.抽象类也不能 实例化 ,但是抽象类 也可以拥有普通的方法,切记抽象类中 并非 只有抽象方法,也可以有普通方法。

    5.但是一个类 一旦包含抽象方法(即纯虚方法)那么这个类一定是抽象类 不能自己实例化。

    ------------------------------------------------------------------------------------------------------------------------------------

    普通虚方法-------父类必须实现,子类 可以选择是否覆盖。

    纯虚方法---------父类只定义 不能实现,子类必须覆盖重写,且会把类变成抽象类 不能自己实例化。

    unit Unit4;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
    
    type
      TForm4 = class(TForm)
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    /// <summary>
    /// 定义一个普通的车类
    /// </summary>
    TCar = class
      function name(): string; virtual; //虚方法父类也是必须实现的,子类可以选择是否覆盖,不必须
    end;
    
    /// <summary>
    /// 定义一个车的抽象类
    /// </summary>
    TCarAbstract = class abstract
      function name(): string; //抽象类与普通的类一样,是可以拥有普通的方法的,但是抽象类不能实例化 仅此而已,其它类有关的功能都有
    end;
    
    /// <summary>
    /// 定义一个普通的车类 + 增加一个纯虚方法 就可以把这个类变成一个抽象类 不能实例化了
    /// 间接抽象类
    /// </summary>
    TCarIndirectAbstract = class
      function name(): string; virtual; abstract;//这个是纯虚方法,只定义不实现  纯虚方法也叫做抽象方法;子类没有选择的权利,必须覆盖overide
    end;
    
    var
      Form4: TForm4;
    
    implementation
    
    {$R *.dfm}
    
    { TCar }
    
    function TCar.name: string;
    begin
      //这里必须得实现,否则编译会报错.
      Exit('car');
    end;
    
    { TCarAbstract }
    
    function TCarAbstract.name: string;
    begin
      //抽象类中也可以包含任何剖通方法 与普通类一样。
      Exit('CarAbstract');
    end;
    
    end.

    另外关于接口与抽象类的区别 我手机拍了下 疯狂java书中的总结 感觉 抽象类 还是 无法淘汰的技术,接口 并非能取代 抽象类。

    用虚方法实现多态的举例:

    ============================2017.04.20 补充:=========================

    重定义 与 覆盖重写 的区别如下:

    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;
        Memo1: TMemo;
        Button2: TButton;
        Button3: TButton;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
        procedure Button3Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
        //重新定义的方式
      TCar1 = class
        public
          function getColor(): string;
      end;
    
      TRedCar1 = class(TCar1)
        public
          function getColor(): string;
      end;
    
      //覆盖的方式
      TCar2 = class
        public
          function getColor(): string; virtual;
      end;
    
      TRedCar2 = class(TCar2)
        public
          function getColor(): string; override;
      end;
    
    var
      Form5: TForm5;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm5.Button1Click(Sender: TObject);
    var
      c1: TCar1;
      r1: TRedCar1;
    begin
      c1 := TCar1.Create;
      r1 := TRedCar1.Create;
      try
        Memo1.Lines.Add(c1.getColor);//无色 --- 常规使用
        Memo1.Lines.Add(r1.getColor);//红色 --- 常规使用
      finally
        c1.Free;
        r1.Free;
      end;
    end;
    
    procedure TForm5.Button2Click(Sender: TObject);
    var
      c2: TCar2;
      r2: TRedCar2;
    begin
      c2 := TCar2.Create;
      r2 := TRedCar2.Create;
      try
        Memo1.Lines.Add(c2.getColor);//无色 --- 常规使用
        Memo1.Lines.Add(r2.getColor);//红色 --- 常规使用
      finally
        c2.Free;
        r2.Free;
      end;
    end;
    
    procedure TForm5.Button3Click(Sender: TObject);
    var
      c1: TCar1;
      c2: TCar2;
    begin
      //父类的实例用子类来创建,这个时候区别就出来了。
      c1 := TRedCar1.Create;
      c2 := TRedCar2.Create;
      try
        Memo1.Lines.Add(c1.getColor); //无色 --- 重定义【老子的是老子的,儿子的是儿子的】
        Memo1.Lines.Add(c2.getColor); //红色 --- 重写【父类的实例被子类创建了,老子的方法指针被儿子的取代了。老子的就是儿子的,儿子的还是儿子的】
      finally
        c1.Free;
        c2.Free;
      end;
    end;
    
    { TRedCar2 }
    
    function TRedCar2.getColor: string;
    begin
      Result := '红色';
    end;
    
    { TRedCar1 }
    
    function TRedCar1.getColor: string;
    begin
      Result := '红色';
    end;
    
    { TCar1 }
    
    function TCar1.getColor: string;
    begin
      Result := '无色';
    end;
    
    { TCar2 }
    
    function TCar2.getColor: string;
    begin
      Result := '无色';
    end;
    
    end.

    网友的回答粘贴如下:

    再探讨个问题,为什么要使用虚方法????,

    这是再delphi中特有的,在Java中直接对普通方法就可以overide重写,再delphi中只能对用virtual修饰的方法才可以被overide。

    我们通常的需求是这样的,我们定义了一个父类和一个很多子类,我们再使用的时候,只关注用途而不关心具体是哪个类来实现。还有一种情况是

    我们事先并不知道是哪个类来实现,需要根据情况动态的判断用哪个类来实现 TStrings,TStream就是典型的应用。见如下demo:

    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;
        Memo1: TMemo;
        Edit1: TEdit;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
      /// <summary>
      /// 定义一个汽车基类
      /// </summary>
      TCar = class
        public
          function run(): string; virtual;
      end;
    
      /// <summary>
      /// 轿车类
      /// </summary>
      TJiaoChe = class(TCar)
        public
          function run(): string; override;
      end;
    
      /// <summary>
      /// 货车类
      /// </summary>
      THuoChe = class(TCar)
        public
          function run(): string; override;
      end;
    
    var
      Form5: TForm5;
    
    implementation
    
    {$R *.dfm}
    
    { THuoChe }
    
    function THuoChe.run: string;
    begin
      Result := '货车跑了';
    end;
    
    { TJiaoChe }
    
    function TJiaoChe.run: string;
    begin
      Result := '轿车跑了';
    end;
    
    { TCar }
    
    function TCar.run: string;
    begin
      Result := '车跑了';
    end;
    
    procedure TForm5.Button1Click(Sender: TObject);
    var
      //我们再使用的时候,往往事先并不知道到底哪个车跑了,或者说车跑了,是根据条件来跑的
      c: TCar;
    begin
      //根据条件来动态的判断哪个车跑了,然后实例化相应的对象
      if Trim(Edit1.Text) = '轿车' then
      begin
        c := TJiaoChe.Create;
        Memo1.Lines.Add(c.run);
      end else if Trim(Edit1.Text) = '货车' then begin
        c := THuoChe.Create;
        Memo1.Lines.Add(c.run);
      end else begin
        c := TCar.Create;
        Memo1.Lines.Add(c.run);
      end;
      c.Free;
    end;
    
    end.

    这样有利于多态的情况下,即定义父类的实例,然后用不同的子类来实现,运行的方法 都是对应的各个子类的。

    结论:若用多态,即一个父类 + N多子类,且父类的方法需要再子类中加强的情况下,尽量不要用重定义的方法,应该用 virtual + overide的方式;

    或者说 尽量用 virtual + overide 的方式 而不要用 重定义的方法,这样做的好处是,多态使用的时候,用父类实例去调用方法的时候,实际调用的是

    各个子类中加强的方法,而不是父类原有的方法。

    另外还有个现象你要留意下:

      /// <summary>
      /// 一个普通类
      /// </summary>
      TPerson = class
        public
          /// <summary>
          /// 注意这里是重定义,而不是覆盖,这个估计是重定义用的最多的地方了,因为TObject的Create方法不是虚方法
          /// 所以一般都是只能重定义,除此之外,重定义尽量少用。你想想父类有个方法,你子类中又定义一个重名方法
          /// 这样做是不安全的,有一天你会搞混的。
          /// </summary>
          constructor Create;
    
          /// <summary>
          /// TObject.Destroy是虚方法,子类覆盖
          /// </summary>
          destructor Destroy; override;
      end;
  • 相关阅读:
    【Linux】PS命令
    【Linux】多进程与多线程之间的区别
    【杂项】XML Schema和DTD的区别
    推荐一组强大的Collection类
    关于Decorator模式我的理解
    菜鸟白话设计模式系列
    PowerCollections研究: 第1弹竟就发现不少问题
    [白话设计模式] Singleton
    PowerCollection研究:第2枪小谈RemoveALL算法
    yield 关键字
  • 原文地址:https://www.cnblogs.com/del88/p/6360180.html
Copyright © 2020-2023  润新知