2013-07-15 64 views
0

我想編寫一些內容,一旦動態分配地址空間的指針離開堆棧,就會自動釋放內存。 一個例子是:離開堆棧時自動釋放內存

procedure FillMemory (var mypointer); 
begin 
    // CopyMemory, Move, etc... with data 
end; 

procedure MyProcedure; 
var 
    MyPointer : Pointer; 
begin 
    MyPointer := VirtualAlloc (NIL, 1024, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE); 
    FillMemory (MyPointer); 
    VirtualFree(MyPointer, 0, MEM_RELEASE); // I would like to avoid this... 
end; 

我可以使用字符串,但我也想避開他們(這我不敢肯定,如果字符串在堆棧中得到釋放反正...) 任何想法?

+3

字符串不在堆棧中。它們是一個引用計數指向堆的指針。 –

+0

我知道,但他們如何獲得釋放? –

+2

通過引用計數機制 - 一旦它爲零,內存就被釋放。這由編譯器對字符串和動態數組進行管理。一個可能的解決方案是在後來的(2009+?)Delphi中使用一個動態的'byte數組',它被預定義爲TBytes(http://docwiki.embarcadero.com/Libraries/XE4/en/System .SysUtils.TBytes) –

回答

4

要擴大我的意見,並與亞略討論,

如果你只是想要一個原始的內存塊,請使用動態的array of byte。編譯器將生成代碼以在方法結束時釋放此內存:

type 
    TBytes = array of Byte; // omit for newer Delphi versions 

procedure FillMemory(var Bytes: TBytes); 
begin 
    { passing in here will increase the reference count to 2 } 
    // CopyMemory, Move, etc... with data 
end; // then drop back to 1 

procedure MyProcedure; 
var 
    Buffer : TBytes; 
begin 
    SetLength(Buffer, 1024); // buffer has reference count of 1 
    FillMemory (Buffer); 
end; // reference count will drop to 0, and Delphi will free memory here 

希望這一切都有道理。這是午夜,所以我沒有感覺到最清醒的...

+3

+1這是OP想要的:自動分配原始內存,當代碼超出範圍時自動釋放。好主意談談引用計數 - 但是用'var'它不會增加引用計數,AFAIK! :)如果不使用'var'和'const',只是原始參數,引用計數會增加。請予以更正。對於類型定義,我還用「TBytes = Byte」數組糾正了一個小錯字。 –

+1

@ArnaudBouchez是嗎?該問題使用'PAGE_EXECUTE_READWRITE'調用'VirtualAlloc'。 –

+1

@DavidHeffernan我沒有看到PAGE_EXECUTE_READWRITE,你是對的。但我懷疑OP不需要它,除非他知道如何在運行中生成asm存根,我懷疑是否給出了他的問題。他寫道,他「繞過RTL」,這是一種無意義 - 在這種情況下,您的基於班級的方法也不起作用。 :) –

3

託管類型的引用已計數,當計數降至零時,它們即已完成。如果您有一個局部變量,那麼當它超出範圍時,其引用計數將降爲零。

所以,你可以創建一個TInterfacedObject的後代,你使用的是一個接口。事情是這樣的:

type 
    TLifetimeWatcher = class(TInterfacedObject) 
    private 
    FDestroyProc: TProc; 
    public 
    constructor Create(const DestroyProc: TProc); 
    destructor Destroy; override; 
    end; 

constructor TLifetimeWatcher.Create(const DestroyProc: TProc); 
begin 
    inherited Create; 
    FDestroyProc := DestroyProc; 
end; 

destructor TLifetimeWatcher.Destroy; 
begin 
    if Assigned(FDestroyProc) then 
    FDestroyProc(); 
    inherited; 
end; 

然後,您可以使用它像這樣:

procedure MyProcedure; 
var 
    MyPointer: Pointer; 
    LifetimeWatcher: IInterface; 
begin 
    MyPointer := VirtualAlloc (NIL, 1024, MEM_COMMIT or MEM_RESERVE, 
    PAGE_EXECUTE_READWRITE); 
    LifetimeWatcher := TLifetimeWatcher.Create(
    procedure 
    begin 
     VirtualFree(MyPointer, 0, MEM_RELEASE); 
    end; 
) 
    FillMemory(MyPointer); 
end; 

LifetimeWatcher葉範圍,則執行被破壞,你傳遞給TLifetimeWatcher.Create實現對象和程序。

將專門研究這個想法專門用於您的用例將會很容易。這將使呼叫站點的代碼更加簡潔。

這將是這樣的:

function VirtualAllocAutoRelease(Size: SIZE_T; Protect: DWORD; 
    out LifetimeCookie: IInterface): Pointer; 
var 
    Ptr: Pointer; 
begin 
    Ptr := VirtualAlloc(nil, Size, MEM_COMMIT or MEM_RESERVE, Protect); 
    Win32Check(Ptr<>nil); 
    LifetimeCookie := TLifetimeWatcher.Create(
    procedure 
    begin 
     VirtualFree(Ptr, 0, MEM_RELEASE); 
    end 
); 
    Result := Ptr; 
end; 

你會使用這樣的:

procedure MyProcedure; 
var 
    MyPointer: Pointer; 
    LifetimeWatcher: IInterface; 
begin 
    MyPointer := VirtualAllocAutoRelease(1024, PAGE_EXECUTE_READWRITE, 
    LifetimeWatcher); 
    FillMemory(MyPointer); 
end; 
+1

OP想要繞過整個getmem過程(請參閱他的評論)。所以使用班級對他來說是不可能的。 :)所有這些聽起來有點困惑......但你的答案是完全有效的(即使我懷疑他使用的是處理代表的Delphi版本)。 –

+1

@ArnaudBouchez問題中沒有什麼表明類不可行。他最近的問題之一是標記XE4,但無論如何,爲什麼我們應該被束縛在Delphi 7中! –

+1

@ArnaudBouchez儘管我看不到太多的實際意義,但是hwy不能讓一個單一目的的IInterface實現類在不使用堆管理器的情況下工作嗎?是的,你將不得不重寫.NewInstance等等 - 但這是可能的,不是嗎?再次,我不談論實際的利潤,只是關於你的「不可能的」 –