如何从TObjectList <T>继承而不是从TObjectList继承

alo*_*ono 11 delphi inheritance generic-collections

为什么这个程序会报告内存泄漏?

{$APPTYPE CONSOLE}

uses
  System.Generics.Collections;

type
  TDerivedGenericObjectList = class(TObjectList<TObject>)
  public
    constructor Create;
  end;

constructor TDerivedGenericObjectList.Create;
begin
  inherited;
end;

var
  List: TDerivedGenericObjectList;

begin
  ReportMemoryLeaksOnShutdown := True;
  List := TDerivedGenericObjectList.Create;
  List.Add(TObject.Create);
  List.Free;
end.
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 14

你正在调用无参数构造函数TObjectList<T>.这实际上是派生TList<T>的类的构造函数TObjectList<T>.

声明的所有构造函数都TObjectList<T>接受一个名为的参数AOwnsObjects,该参数用于初始化该OwnsObjects属性.因为您绕过该构造函数,OwnsObjects默认为False,并且列表的成员不会被销毁.

您应该确保调用TObjectList<T>该初始化的构造函数OwnsObjects.例如:

{$APPTYPE CONSOLE}

uses
  System.Generics.Collections;

type
  TDerivedGenericObjectList = class(TObjectList<TObject>)
  public
    constructor Create;
  end;

constructor TDerivedGenericObjectList.Create;
begin
  inherited Create(True);
end;

var
  List: TDerivedGenericObjectList;

begin
  ReportMemoryLeaksOnShutdown := True;
  List := TDerivedGenericObjectList.Create;
  List.Add(TObject.Create);
  List.Free;
end.
Run Code Online (Sandbox Code Playgroud)

也许更好的变体是让你的构造函数也提供AOwnsObjects参数:

type
  TDerivedGenericObjectList = class(TObjectList<TObject>)
  public
    constructor Create(AOwnsObjects: Boolean = True);
  end;

constructor TDerivedGenericObjectList.Create(AOwnsObjects: Boolean);
begin
  inherited Create(AOwnsObjects);
end;
Run Code Online (Sandbox Code Playgroud)

要么:

type
  TDerivedGenericObjectList = class(TObjectList<TObject>)
  public
    constructor Create(AOwnsObjects: Boolean = True);
  end;

constructor TDerivedGenericObjectList.Create(AOwnsObjects: Boolean);
begin
  inherited;
end;
Run Code Online (Sandbox Code Playgroud)

所以,您可能想知道为什么原始版本选择了一个TList<T>构造函数而不是一个TObjectList<T>.那么,让我们更详细地看一下.这是你的代码:

type
  TDerivedGenericObjectList = class(TObjectList<TObject>)
  public
    constructor Create;
  end;

constructor TDerivedGenericObjectList.Create;
begin
  inherited;
end;
Run Code Online (Sandbox Code Playgroud)

inherited以这种方式使用时,编译器会用完全相同的签名,因为这一个构造函数.它找不到一个,TObjectList<T>因为它们都有参数.它可以找到一个TList<T>,这就是它使用的那个.

正如您在评论中提到的,以下变体不会泄漏:

constructor TDerivedGenericObjectList.Create;
begin
  inherited Create;
end;
Run Code Online (Sandbox Code Playgroud)

与裸体相比,此语法inherited将找到在替换默认参数时匹配的方法.因此TObjectList<T>调用单个参数构造函数.

文档包含以下信息:

继承的保留字在实现多态行为中起着特殊的作用.它可以在方法定义中出现,有或没有标识符.

如果继承后跟成员的名称,则表示正常的方法调用或对属性或字段的引用,除了搜索引用的成员以封闭方法的类的直接祖先开始.例如,在:

inherited Create(...);
Run Code Online (Sandbox Code Playgroud)

发生在方法的定义中,它调用继承的Create.

当继承后面没有标识符时,它引用与封闭方法同名的继承方法,或者如果封闭方法是消息处理程序,则引用同一消息的继承消息处理程序.在这种情况下,inherited不使用显式参数,而是将与调用封闭方法相同的参数传递给inherited方法.例如:

inherited;
Run Code Online (Sandbox Code Playgroud)

在构造函数的实现中经常发生.它使用传递给后代的相同参数调用继承的构造函数.

  • 但最好是明确的.我的`TObjectList <T>`版本强制调用者传递OwnsObjects. (2认同)