我有一个函数,将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;


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

最佳答案

您的代码中包含几个错误:


if( Value.Graphic is TPngImage ) then



如果呼叫者的TPicture尚未包含TPNGImage,则根本不返回任何内容。 Result未定义。

实际上,您根本不需要检查Graphic的类型。可以将各种TGraphic类分配给彼此,将其图像数据从一种格式转换为另一种格式,因此应尽可能进行这种转换。


AnImage.Assign( TPngImage( Value ) );



您正在强制转换TPicture本身。您需要改型其Graphic


Result := AnImage;
//AnImage.Free();



这要求调用者获得TPNGImage的所有权并释放它,这通常是一个不好的设计。


Image2.Picture.Assign( Effect( Image1.Picture ) );



举例来说,调用者没有获得返回的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;


要么

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;


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

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


但是,更好的设计是根本不返回新的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;


要么

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;


然后,您可以执行以下操作:

procedure TForm11.Button1Click(Sender : TObject);
begin
  Effect(Image1.Picture, Image2.Picture);
end;

10-08 16:03