2011-03-06 28 views
2

綜述:如何通過var前綴正確傳遞對象類型的參數?

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. MyObjectMyRecord是值類型,而MyClass是引用類型。
  2. 值類型變量的賦值將複製變量;引用類型變量的賦值將複製引用。
  3. ProcAProcC中的參數是原始參數的副本。
  4. ProcBProcD的參數是原來的參數。
  5. ProcE中的參數是原始參考的副本。
  6. ProcF的參數是原始參考。
  7. 關於如何結束在單元agg_2D.pas中聲明的Agg2D對象進行繪製,請參閱下面David的答案。

===========================================
我正在學習使用AggPas,它是一個純粹的矢量圖形繪製API。特別是使用包含Agg2D對象的單元agg_2D.pas,而不是包含TAgg2D類的單元Agg2D.pas。在單元Agg2D.pas上選擇單元agg_2D.pas的原因是爲了跨平臺能力。

但是,我無法正確地通過與VAR前綴的Agg2D對象類型的參數。如下面的代碼所示,我想將由TForm1創建的Agg2D對象傳遞給另一個實際負責繪製形狀的類。但是,它不起作用。你能幫忙評論一下可能的原因嗎?看來我一定錯過了關於對象類型的重要概念。任何建議表示讚賞!您可以新建一個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. 

如果我刪除的過程參數的變種前綴的所有出現,第二個圓圈繪製代碼也停止工作,這一點我不太明白。單位如下所示爲您的方便:

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

當你說「不工作」時,你是什麼意思?你收到什麼錯誤信息?在編譯時還是在運行時?沒有這樣的信息,你正在讓人們更難以幫助你。 – 2011-03-06 19:36:38

+0

@Marjan Venema:謝謝你的時間!代碼繪製一個圓圈。與「不工作」評論的這條線不會給圓圈或其他任何東西。 – SOUser 2011-03-06 19:38:10

回答

5

我很努力地理解你在這裏做什麼。我認爲你的基本問題是Agg2Dobject,因此是一個值類型。你拿一份它的副本,以便有兩份而不是一份。作者選擇使用object而不是一個類,但這樣做要求您對子類的值語義而非引用語義非常敏感。

快速黑客得到這個工作是改變fVG: Agg2D;fVG: ^Agg2D;TRenderEngine_Agg.Create變化Self.fVG := aVGSelf.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。如果你這樣做,那麼我認爲你會是金!

+0

@David Heffernan:非常感謝您的幫助!我會嘗試你的建議!同時,你提到Agg2D是對象類型的,因此是一個值類型? (我的意思是,Rob剛剛提到它也是參考類型。) – SOUser 2011-03-06 20:30:46

+1

@Xichen Rob在這個例子中是不正確的。我認爲他不熟悉Agg2D,這是一個非標準的代碼。如果使用'object'而不是'class'。如果有幫助,你可以用和你想象的「record」相同的方式來思考'object'。 – 2011-03-06 20:32:15

+1

這是一個對象,而不是一個類?這將解釋它。人們仍然*做*那? – 2011-03-06 20:34:37

相關問題