• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • 淺議C++/CLI的gcnew關鍵字

    發表于:2007-05-25來源:作者:點擊數: 標簽:淺議gcnewC++關鍵字CLI
    C++/CLI中使用gcnew關鍵字表示在托管堆上分配內存,并且為了與以前的指針區分,用^來替換* ,就語義上來說他們的區別大致如下: 1. gcnew返回的是一個句柄(Handle),而new返回的是實際的內存地址. 2. gcnew創建的對象由虛擬機托管,而new創建的對象必須自己來
     C++/CLI中使用gcnew關鍵字表示在托管堆上分配內存,并且為了與以前的指針區分,用^來替換* ,就語義上來說他們的區別大致如下:
     

      1.     gcnew返回的是一個句柄(Handle),而new返回的是實際的內存地址.
      2.     gcnew創建的對象由虛擬機托管,而new創建的對象必須自己來管理和釋放.
     
      當然,從程序員的角度來說,管它是句柄還是什么其他的東西,總跑不掉是對某塊內存地址的引用,實際上我們都可以理解成指針.下面我們就寫一段代碼來測試一下好了.
     
    using namespace System;
     
    ref class Foo
    {
    public:
        Foo()
        {
           System::Console::WriteLine("Foo::Foo");
        }
        ~Foo()
        {
           System::Console::WriteLine("Foo::~Foo");
        }
    public:
        int m_iValue;
    };
     
    int _tmain()
    {
        int* pInt = new int;
        int^ rInt = gcnew int;
        Foo^ rFoo = gcnew Foo;
     
        delete rFoo;
        delete rInt;
        delete pInt;
    }
     
      我把調試的時候JIT編譯的匯編代碼擇錄了部分如下顯示(請注意紅色部分):

        int* pInt = new int;
    0000004c  mov         ecx,4
    00000051  call        dword ptr ds:[03B51554h]
    00000057  mov         esi,eax
    00000059  mov         dword ptr [esp+18h],esi
        int^ rInt = gcnew int;
    0000005d  mov         ecx,788EF9D8h
    00000062  call        FCFAF66C
    00000067  mov         esi,eax
    00000069  mov         dword ptr [esi+4],0
    00000070  mov         edi,esi
        Foo^ rFoo = gcnew Foo;
    00000072  mov         ecx,3B51768h
    00000077  call        FCFAF66C
    0000007c  mov         esi,eax
    0000007e  mov         ecx,esi
    00000080  call        dword ptr ds:[03B517ACh]
    00000086  mov         dword ptr [esp+1Ch],esi
     
        delete rFoo;
    0000008a  mov         ebx,dword ptr [esp+1Ch]
    0000008e  test        ebx,ebx
    00000090  je          000000A4
    00000092  mov         ecx,ebx
    00000094  call        dword ptr ds:[03FD0028h]
    0000009a  mov         dword ptr [esp+14h],0
    000000a2  jmp         000000AC
    000000a4  mov         dword ptr [esp+14h],0
        delete rInt;
    000000ac  mov         edx,edi
    000000ae  mov         ecx,788F747Ch
    000000b3  call        FC8D20FD
    000000b8  mov         ebp,eax
    000000ba  test        ebp,ebp
    000000bc  je          000000D0
    000000be  mov         ecx,ebp
    000000c0  call        dword ptr ds:[03FD0020h]
    000000c6  mov         dword ptr [esp+10h],0
    000000ce  jmp         000000D8
    000000d0  mov         dword ptr [esp+10h],0
        delete pInt;
    000000d8  mov         ecx,dword ptr [esp+18h]
    000000dc  call        dword ptr ds:[03B51540h]
     
     
       我們先看分配內存這部分的代碼
     
      1.調用new方式分配
    int* pInt = new int;
    0000004c  mov         ecx,4
    00000051  call        dword ptr ds:[03B51554h]

      可以看到,和以前在vc6中一樣,分配內存的步驟如下:
      1.  首先把sizeof(int) = 4 放到ecx中
      2.  調用operator new 去分配4個字節
      3.  調用構造函數等等......(這里不是我們的重點)

      成功分配后,會把返回地址放在eax中。
     
      2.調用gcnew方式分配
        int^ rInt = gcnew int;
    0000005d  mov         ecx,788EF9D8h
    00000062  call        FCFAF66C
    。。。
        Foo^ rFoo = gcnew Foo;
    00000072  mov         ecx,3B51768h
    00000077  call        FCFAF66C

      可以看到gcnew也是通過把一個參數放到ecx中,然后再調用一個函數來完成分配的操作,顯然0x788EF9D8應該是一個地址,而不可能是一個數值。我們可以看到這里gcnew創建兩個不同類型的變量,調用的函數地址卻都是0xFCFAF66C,而存放到ecx中的兩個地址就不一樣。究竟這幾個地址代表什么呢?
     
      和new一樣gcnew也是把返回地址放在eax中。我們直接從內存窗口看eax指向的內存塊好了。Aha,看到了沒有?

      這次的eax = 0x00F73404  對應的內存塊為
     
    0x00F73404  d8 f9 8e 78 00 00 00 00 。。。
     
      這個不就是 mov 到 ecx中的值么?再回憶昨天寫的分析Object對象布局的文章,可以肯定這個就是 MethodTable地址了,對于這個int來說,后面的4個字節對應的就是存放它的RawData,比如如果你初始化為 4 那么內存對應的就變化為 d8 f9 8e 79 04 00 00 00
     
      分析清楚存放到ecx中的是 MethodTable指針,我們再分析那個對應的call函數,從vm的代碼可以看出,有三個全局函數用來根據MethodTable創建對象,同時MethodTable本身也提供一個成員函數Allocate(),只不過這個成員函數也是調用的下面的函數:

    OBJECTREF AllocateObject( MethodTable *pMT )
    OBJECTREF AllocateObjectSpecial( MethodTable *pMT )
    OBJECTREF FastAllocateObject( MethodTable *pMT )
     
      其中AllocateObject又是調用AllocateObjectSpecial來完成工作。那么我們調用的應該就是AllocateObject或者FastAllocateObject了。

      在我們的例子里面兩個call的地址都一樣,但是你如果寫下代碼 double ^ pDouble = gcnew double;這個時候的地址是多少?它和int 的一樣么?

      目前我還沒有仔細去研究這個地址到底對應的是該類型的MethodTable::Allocate()或是上面的這三個全局函數,如果對應MethodTable::Allocate(),那么2.0中應該有個MethodTable::FastAllocate()吧,否則應該就是對應的全局函數AllocateObject 以及FastAllocateObject了。過幾天一定要抽空再好好研究一下。
     
      下面看對應的delete函數。
        delete pInt;
    000000d8  mov         ecx,dword ptr [esp+18h]
    000000dc  call        dword ptr ds:[03B51540h]
     
    比較簡單,就是傳入地址,然后調用operator delete來釋放類存,會調用析構函數
     
      對應的,釋放gcnew創建的對象的代碼如下:
        delete rInt;
    000000ac  mov         edx,edi
    000000ae  mov         ecx,788F747Ch
    000000b3  call        FC8D20FD

      這個也相對簡單,它對應vm里面的一個函數:
    void  CallFinalizer(Thread* FinalizerThread, Object* fobj)

      那么也就是
    fobjà edx
    FinalizerThread à ecx
    Call CallFinalizer
     
      但是,請注意?。。。。。?!一個類包含析構函數和不包含析構函數,它對應的delete代碼是不一樣的,這點可以通過匯編代碼比較得到,我這里就不多說了。
     

    原文轉自:http://www.kjueaiud.com

    評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>