Как правильно передать аргумент типа объекта с префиксом var? - PullRequest
2 голосов
/ 06 марта 2011

Суммирование:

type 
  MyObject = object
  end;

  MyRecord = record
  end;

  MyClass = class
  end;

  procedure ProcA(aMyObject: MyObject);
  procedure ProcB(var aMyObject: MyObject);
  procedure ProcC(aMyRecord: MyRecord);
  procedure ProcD(var aMyRecord: MyRecord);
  procedure ProcE(aMyClass: MyOClass);
  procedure ProcF(var aMyClass: MyClass);
  1. MyObject и MyRecord являются типом значения, тогда как MyClass является ссылочным типом.
  2. Присвоение переменной типа значения будет копировать переменную; присвоение переменной ссылочного типа скопирует ссылку.
  3. Аргументы в ProcA и ProcC являются копиями оригинальных.
  4. Аргументы в ProcB и ProcD являются исходными.
  5. Аргумент в ProcE является копией исходной ссылки.
  6. Аргумент в ProcF является исходной ссылкой.
  7. Относительно того, как обернуть объект Agg2D, который объявлен в модуле agg_2D.pas, для рисования, смотрите ответ Дэвида ниже.

===========================================
Я учусь использовать AggPas - API-интерфейс для рисования векторной графики. В частности, модуль agg_2D.pas, который содержит объект Agg2D, используется вместо модуля Agg2D.pas, который содержит класс TAgg2D. Причина выбора юнита agg_2D.pas вместо юнита Agg2D.pas заключается в кроссплатформенности.

Однако я не могу правильно передать аргумент типа объекта Agg2D с префиксом var. Как показано в следующем коде, я хочу передать объект Agg2D, созданный TForm1, другому классу, который действительно отвечает за рисование фигур. Однако это не работает. Не могли бы вы помочь прокомментировать возможную причину? Кажется, я пропустил важные понятия о типе объекта. Любое предложение приветствуется! Вы могли бы создать приложение VCL, прикрепить обработчик FormCreate и построчно закомментировать коды чертежей, чтобы увидеть эффект.

    unit Unit1;

    interface

    uses
      agg_2D,
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs;

    type
      TRenderEngine_BMP = class;
      TRenderEngine_Agg = class;
      TForm1 = class;

      TRenderEngine_BMP = class
      private
        fBMP: TBitmap;
      public
        constructor Create(var aBMP: TBitmap);
        procedure DrawEllipse;
      end;

      TRenderEngine_Agg = class
      private
        fVG: Agg2D;
      public
        constructor Create(var aVG: Agg2D);
        procedure DrawEllipse;
      end;

      TForm1 = class(TForm)
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }

        fBMP: TBitmap;
        fVG: Agg2D;
        fEngine_BMP: TRenderEngine_BMP;
        fEngine_Agg: TRenderEngine_Agg;

        procedure AttachBMP(var aVG: Agg2D; var aBMP: TBitmap);
        procedure OnSceneResize(Sender: TObject);
        procedure OnScenePaint(Sender: TObject);
      public
        { Public declarations }
      end;

    var
      Form1: TForm1;

    implementation

    {$R *.dfm}

    uses
      Math;

    { TRenderEngine_BMP }

    constructor TRenderEngine_BMP.Create(var aBMP: TBitmap);
    begin
      Self.fBMP := aBMP;
    end;

    procedure TRenderEngine_BMP.DrawEllipse;
    begin
      Self.fBMP.Canvas.ellipse(20, 20, 80, 80);
    end;

    { TRenderEngine_Agg }

    constructor TRenderEngine_Agg.Create(var aVG: Agg2D);
    begin
      Self.fVG := aVG;
    end;

    procedure TRenderEngine_Agg.DrawEllipse;
    begin
      Self.fVG.ellipse(50, 50, 30, 30);
    end;

    { TForm1 }

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Self.OnResize := {$IFDEF FPC} @ {$ENDIF} OnSceneResize;
      Self.OnPaint := {$IFDEF FPC} @ {$ENDIF} OnScenePaint;

      fBMP := TBitmap.Create;
      fBMP.PixelFormat := pf32bit;
      fBMP.Canvas.Brush.Style := bsSolid;
      fBMP.Canvas.Brush.Color := clBlue;
      fBMP.Width := ClientWidth;
      fBMP.Height := ClientHeight;

      fVG.Construct;
      Self.AttachBMP(fVG, fBMP);

      fEngine_BMP := TRenderEngine_BMP.Create(fBMP);
      fEngine_Agg := TRenderEngine_Agg.Create(fVG);
    end;

    procedure TForm1.AttachBMP(var aVG: Agg2D; var aBMP: TBitmap);
    var
      tmpBuffer: pointer;
      tmpStride: integer;
    begin
      tmpStride := integer(aBMP.ScanLine[1]) - integer(aBMP.ScanLine[0]);

      if tmpStride < 0 then
        tmpBuffer := aBMP.ScanLine[aBMP.Height - 1]
      else
        tmpBuffer := aBMP.ScanLine[0];

      aVG.attach(tmpBuffer, aBMP.Width, aBMP.Height, tmpStride);
    end;

    procedure TForm1.OnScenePaint(Sender: TObject);
    begin
      Self.fBMP.Canvas.FillRect(Self.ClientRect);

    //  Self.fBMP.Canvas.ellipse(20, 20, 80, 80);  // Work
    //  Self.fVG.ellipse(50, 50, 30, 30);          // Work
    //  Self.fEngine_BMP.DrawEllipse;              // Work
      Self.fEngine_Agg.DrawEllipse;                // Do not work

      Self.Canvas.Draw(0, 0, fBMP);
    end;

    procedure TForm1.OnSceneResize(Sender: TObject);
    begin
      fBMP.Width := IfThen(ClientWidth > 0, ClientWidth, 2);
      fBMP.Height := IfThen(ClientHeight > 0, ClientHeight, 2);

      Self.AttachBMP(fVG, fBMP);
    end;

    end.

Если я удаляю все вхождения префикса var аргументов процедуры, второй код рисования круга также перестает работать, что я не совсем понимаю. Для удобства устройство показано ниже:

    unit Unit1;

    interface

    uses
      agg_2D,
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs;

    type
      TRenderEngine_BMP = class;
      TRenderEngine_Agg = class;
      TForm1 = class;

      TRenderEngine_BMP = class
      private
        fBMP: TBitmap;
      public
        constructor Create(aBMP: TBitmap);
        procedure DrawEllipse;
      end;

      TRenderEngine_Agg = class
      private
        fVG: Agg2D;
      public
        constructor Create(aVG: Agg2D);
        procedure DrawEllipse;
      end;

      TForm1 = class(TForm)
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }

        fBMP: TBitmap;
        fVG: Agg2D;
        fEngine_BMP: TRenderEngine_BMP;
        fEngine_Agg: TRenderEngine_Agg;

        procedure AttachBMP(aVG: Agg2D; aBMP: TBitmap);
        procedure OnSceneResize(Sender: TObject);
        procedure OnScenePaint(Sender: TObject);
      public
        { Public declarations }
      end;

    var
      Form1: TForm1;

    implementation

    {$R *.dfm}

    uses
      Math;

    { TRenderEngine_BMP }

    constructor TRenderEngine_BMP.Create(aBMP: TBitmap);
    begin
      Self.fBMP := aBMP;
    end;

    procedure TRenderEngine_BMP.DrawEllipse;
    begin
      Self.fBMP.Canvas.ellipse(20, 20, 80, 80);
    end;

    { TRenderEngine_Agg }

    constructor TRenderEngine_Agg.Create(aVG: Agg2D);
    begin
      Self.fVG := aVG;
    end;

    procedure TRenderEngine_Agg.DrawEllipse;
    begin
      Self.fVG.ellipse(50, 50, 30, 30);
    end;

    { TForm1 }

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Self.OnResize := {$IFDEF FPC} @ {$ENDIF} OnSceneResize;
      Self.OnPaint := {$IFDEF FPC} @ {$ENDIF} OnScenePaint;

      fBMP := TBitmap.Create;
      fBMP.PixelFormat := pf32bit;
      fBMP.Canvas.Brush.Style := bsSolid;
      fBMP.Canvas.Brush.Color := clBlue;
      fBMP.Width := ClientWidth;
      fBMP.Height := ClientHeight;

      fVG.Construct;
      Self.AttachBMP(fVG, fBMP);

      fEngine_BMP := TRenderEngine_BMP.Create(fBMP);
      fEngine_Agg := TRenderEngine_Agg.Create(fVG);
    end;

    procedure TForm1.AttachBMP(aVG: Agg2D; aBMP: TBitmap);
    var
      tmpBuffer: pointer;
      tmpStride: integer;
    begin
      tmpStride := integer(aBMP.ScanLine[1]) - integer(aBMP.ScanLine[0]);

      if tmpStride < 0 then
        tmpBuffer := aBMP.ScanLine[aBMP.Height - 1]
      else
        tmpBuffer := aBMP.ScanLine[0];

      aVG.attach(tmpBuffer, aBMP.Width, aBMP.Height, tmpStride);
    end;

    procedure TForm1.OnScenePaint(Sender: TObject);
    begin
      Self.fBMP.Canvas.FillRect(Self.ClientRect);

    //  Self.fBMP.Canvas.ellipse(20, 20, 80, 80);  // Work
    //  Self.fVG.ellipse(50, 50, 30, 30);          // Do not Work
    //  Self.fEngine_BMP.DrawEllipse;              // Work
      Self.fEngine_Agg.DrawEllipse;                // Do not work

      Self.Canvas.Draw(0, 0, fBMP);
    end;

    procedure TForm1.OnSceneResize(Sender: TObject);
    begin
      fBMP.Width := IfThen(ClientWidth > 0, ClientWidth, 2);
      fBMP.Height := IfThen(ClientHeight > 0, ClientHeight, 2);

      Self.AttachBMP(fVG, fBMP);
    end;

    end.

1 Ответ

5 голосов
/ 06 марта 2011

Я изо всех сил пытаюсь понять, что ты здесь делаешь.Я думаю, что ваша основная проблема в том, что Agg2D - это object и тип значения.Вы берете его копию, чтобы было две копии, а не одна.Автор решил использовать object вместо класса, но для этого требуется, чтобы вы были очень внимательны к семантике значений, а не к семантике ссылок TObject потомков.

Быстрый взлом, чтобы заставить это работатьизменить fVG: Agg2D; на fVG: ^Agg2D;, а в TRenderEngine_Agg.Create изменить Self.fVG := aVG на Self.fVG := @aVG.С этим изменением эллипс нарисован.

Теперь, я думаю, вам нужно пересмотреть свой дизайн.Если вы хотите обернуть объект Agg2D в классе рендеринга, то это будет хорошо, но вы не должны брать копии объекта Agg2D.

Вот как я написал бы ваш код для решения этой проблемы:

TRenderEngine_Agg = class
private
  fVG: Agg2D;
public
  constructor Create;
  procedure AttachBMP(aBMP: TBitmap);
  procedure DrawEllipse;
end;

constructor TRenderEngine_Agg.Create;
begin
  fVG.Construct;
end;

procedure TRenderEngine_Agg.AttachBMP(aBMP: TBitmap);
var
  tmpBuffer: pointer;
  tmpStride: integer;
begin
  tmpStride := integer(aBMP.ScanLine[1]) - integer(aBMP.ScanLine[0]);

  if tmpStride < 0 then
    tmpBuffer := aBMP.ScanLine[aBMP.Height - 1]
  else
    tmpBuffer := aBMP.ScanLine[0];

  fVG.attach(tmpBuffer, aBMP.Width, aBMP.Height, tmpStride);
end;

procedure TRenderEngine_Agg.DrawEllipse;
begin
  Self.fVG.fillColor(30, 50, 20);
  Self.fVG.blendMode(BlendContrast );
  Self.fVG.ellipse(50, 50, 30, 30);
end;

procedure TForm20.OnSceneResize(Sender: TObject);
begin
  fBMP.Width := IfThen(ClientWidth > 0, ClientWidth, 2);
  fBMP.Height := IfThen(ClientHeight > 0, ClientHeight, 2);

  fEngine_Agg.AttachBMP(fBMP);
end;

Идея состоит в том, чтобы поместить все, что связано с объектом Agg2D, в TRenderEngine_Agg.Если ты сделаешь это, то, я думаю, ты будешь золотой !!

...