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

参考万一的博客:

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;
原文地址:https://www.cnblogs.com/del88/p/6360180.html