如果未调用TPngImage.Free(),则在TPngImage超出范围时调用TPngImage.Destroy()

Gro*_*rMD 3 delphi graphics png

我有一个函数,它将TPicture作为参数并返回一个TPngImage.为了保留原始图像,我创建了一个TPngImage并将TPicture复制到TPngImage,应用效果并返回TPngImage.就是这样.

function Effect( const Value : TPicture ) : TPngImage;
  var
    AnImage : TPngImage;

  begin
    if( Value.Graphic is TPngImage ) then
      begin
        AnImage := TPngImage.Create();
        AnImage.Assign( TPngImage( Value ) );
        //Apply effect
        Result := AnImage;
        //AnImage.Free(); //error
      end;
  end;

procedure TForm11.Button1Click( Sender : TObject );
  begin
    Image2.Picture.Assign( Effect( Image1.Picture ) );
  end;
Run Code Online (Sandbox Code Playgroud)

在创建对象时,何时释放创建的对象.我不能在函数中调用TPngImage.Free(),因为它会在赋值之前销毁对象.那么如何释放创建的对象呢?当对象超出范围时,TPngImage是否调用其析构函数?据我所知,不释放对象会导致内存泄漏.

Rem*_*eau 5

您的代码中有几个错误:

if( Value.Graphic is TPngImage ) then
Run Code Online (Sandbox Code Playgroud)

如果调用者TPicture尚未包含a TPNGImage,则根本不返回任何内容.这Result是未定义的.

事实上,你根本不应该检查它Graphic的类型.TGraphic可以为彼此分配各种类,将其图像数据从一种格式转换为另一种格式,因此您应该尽可能地进行转换.

AnImage.Assign( TPngImage( Value ) );
Run Code Online (Sandbox Code Playgroud)

你是TPicture自己的类型.你需要对它进行类型转换Graphic.

Result := AnImage;
//AnImage.Free();
Run Code Online (Sandbox Code Playgroud)

这需要调用者获取TPNGImage并释放它,这通常是一个糟糕的设计.

Image2.Picture.Assign( Effect( Image1.Picture ) );
Run Code Online (Sandbox Code Playgroud)

例如,调用者没有获得所返回的所有权TPngImage,因此它被泄露了.

如果您想要返回一个新的TPNGImage,请尝试这样做:

function Effect(Value : TPicture) : TPngImage;
begin
  Result := TPngImage.Create;
  try
    if (Value.Graphic <> nil) and (not Value.Graphic.Empty) then
    begin
      Result.Assign(Value.Graphic);
      //Apply effect
    end;
  except
    Result.Free;
    raise;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

要么

function Effect(Value : TPicture) : TPngImage;
begin
  Result := nil;
  if (Value.Graphic <> nil) and (not Value.Graphic.Empty) then
  begin
    Result := TPngImage.Create;
    try
      Result.Assign(Value.Graphic);
      //Apply effect
    except
      Result.Free;
      raise;
    end;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,您都可以这样做:

procedure TForm11.Button1Click(Sender : TObject);
var
  AImage: TPngImage;
begin
  AImage := Effect(Image1.Picture);
  try
    Image2.Picture.Assign(AImage);
  finally
    AImage.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

但是,更好的设计是不返回新TPngImage的.传入2个TPicture对象并Effect()根据需要操作它们:

procedure Effect(Input, Output : TPicture);
var
  AnImage : TPngImage;
begin
  AnImage := TPngImage.Create;
  try
    if (Input.Graphic <> nil) and (not Input.Graphic.Empty) then
    begin
      AnImage.Assign(Input.Graphic);
      //Apply effect
    end;
    Output.Assign(AnImage);
  finally
    AnImage.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

要么

procedure Effect(Input, Output : TPicture);
var
  AnImage : TPngImage;
begin
  if (Input.Graphic <> nil) and (not Input.Graphic.Empty) then
  begin
    AnImage := TPngImage.Create.Create;
    try
      AnImage.Assign(Input.Graphic);
      //Apply effect
      Output.Assign(AnImage);
    finally
      AnImage.Free;
    end;
  end else
    Output.Assign(nil);
end;
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

procedure TForm11.Button1Click(Sender : TObject);
begin
  Effect(Image1.Picture, Image2.Picture);
end;
Run Code Online (Sandbox Code Playgroud)