如何更改Delphi析构函数以处理共享对象实例?

时间:2014-03-06 15:10:49

标签: delphi soap destructor

如何更改析构函数以处理共享对象实例?

示例:

type
    TAddress = class
    private
        FStreet : String;
    public
    property street: String read FStreet write FStreet;
    end;

    TContract = class
    private
        FAddress : TAddress;
    public
    property address: TAddress read FAddress write FAddress;
    destructor Destroy; override;
    end;

    Array_Of_TContract = array of TContract;

    TCustomer = class
    private
      FContracts : Array_Of_TContract;
    public
    property contracts: Array_Of_TContract read FContracts write FContracts;
    destructor Destroy; override;
    end;

implementation

    destructor TCustomer.Destroy;
    var
      I: Integer;
    begin
      for I := 0 to System.Length(FContracts)-1 do
        SysUtils.FreeAndNil(FContracts[I]);
      System.SetLength(FContracts, 0);
      inherited Destroy;
    end;

    destructor TContract.Destroy;
    var
      I: Integer;
    begin
        SysUtils.FreeAndNil(FAddress);
      inherited Destroy;
    end;

begin
  Try
    //address
    myAddress := TAddress.Create;
    myAddress.street := 'My Street';

    //First contract
    SetLength(myContracts, Length(myContracts)+1);
    FirstContract := TContract.Create;
    FirstContract.address := myAddress;   //<-
    myContracts[0] := FirstContract;

    //Second contract
    SetLength(myContracts, Length(myContracts)+1);
    SecondContract := TContract.Create;
    SecondContract.address := myAddress;  //<-
    myContracts[1] := SecondContract;

    //Single customer
    myCustomer := TCustomer.Create;
    myCustomer.contracts := myContracts;

    myCustomer.Free;

    self.Close;
  Except
    On e: exception do begin
      ShowMessage(e.Message);
    end;
  End;

end;

释放myCustomer(和内存泄漏)时,结果是指针操作无效。

(在我的情况下,不为每个TContract创建单独的TAddress对象)

2 个答案:

答案 0 :(得分:1)

处理此类情况的最佳方法取决于应用程序的结构。基于该示例,我将所有地址添加到通用对象列表,然后将地址引用传递给TContract的实例。对于合同,如果它们不是唯一的,您可以使用类似的方法。 替代方法是使用接口作为地址,然后将其分配给合同实例,最后将其取消。 第三种选择是复制作业。我认为这是处理这种情况的最低效的方法,但它保证可以在任何情况下工作。 希望这些提出了一些想法。

答案 1 :(得分:0)

我不希望在联系人之间共享相同的地址实例。但由于代码是自动生成的,我想这不是一个选项。

您可以跟踪已经释放的引用:

AlreadyFreed := TList <Pointer>.Create;
for I := 0 to System.Length(FContracts)-1 do
  begin
  if not AlreadyFreed.Contains(FContracts[I]) then
    begin 
    FContracts[I].Free;
    AlreadyFreed.Add(Pointer(FContracts[I]));
    end;
  end;