linux社区爱心援助Linux认证系列教程业界动态站务新闻公司招聘建议留言网址大全LPI专题CISCO专题
设为首页
加入收藏
管理团队
JSP  
JAVA  
PERL  
 您的位置:首页 > article > Linux开发区 > 软件开发 >
栏目导栏
资料搜索
热门文章
·Linux 下 C 语言编程
·Linux下的通用线程池创建
·C++字符串转换篇
·linux C 进程操作篇
·linux上的C/C++编译器gcc/egcs
·linux C 文件权限控制篇
·GCC使用手册
·linux C 接口处理篇
·在Redhat Linux上安装 GCC 编译
·GCC使用指南
·C语言运算符
·Linux下C开发环境的构成和安装
·fopen()函数的参数说明
·GCC使用手册与常用命令
·Scheme 语言介绍
最新文章
·在Ubuntu Linux 8.04上构建GCC
·Linux操作系统下Socket编程地址
·将VC程序移植到Linux系统的几点
·Linux下malloc/free与new/dele
·Linux下用GTK和socket实现简单
·Linux操作系统下让Tomcat启动在
·Linux操作系统中如何编译C程序
·几种常被人们忽略的Linux系统下
·Eclipse编程工具 在Ubuntu下的
·Linux操作系统下的网络地址转换
·老手经验谈:Linux驱动程序开发
·Linux操作系统多线程同步Mutex
·Linux操作系统下C程序语言简易
·Linux系统平台下关于GCC编译及
·解决Linux系统下管道被接受方关
Google
 
浅议C++/CLI的gcnew关键字
[ 作者:  加入时间:2007-06-08 19:34:48  来自:Linux联盟收集整理 ]

    C++/CLI中使用gcnew关键字表示在托管堆上分配内存,并且为了与以前的指针区分,用^来替换* ,就语义上来说他们的区别大致如下:

qG7Linux联盟
  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         ecx4

00000051  call        dword ptr ds:[03B51554h]

00000057  mov         esieax

00000059  mov         dword ptr [esp+18h]esi

    int^ rInt = gcnew int;

0000005d  mov         ecx788EF9D8h

00000062  call        FCFAF66C

00000067  mov         esieax

00000069  mov         dword ptr [esi+4]0

00000070  mov         ediesi

    Foo^ rFoo = gcnew Foo;

00000072  mov         ecx3B51768h

00000077  call        FCFAF66C

0000007c  mov         esieax

0000007e  mov         ecxesi

00000080  call        dword ptr ds:[03B517ACh]

00000086  mov         dword ptr [esp+1Ch]esi

 

    delete rFoo;

0000008a  mov         ebxdword ptr [esp+1Ch]

0000008e  test        ebxebx

00000090  je          000000A4

00000092  mov         ecxebx

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         edxedi

000000ae  mov         ecx788F747Ch

000000b3  call        FC8D20FD

000000b8  mov         ebpeax

000000ba  test        ebpebp

000000bc  je          000000D0

000000be  mov         ecxebp

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         ecxdword ptr [esp+18h]

000000dc  call        dword ptr ds:[03B51540h]

 

 

   我们先看分配内存这部分的代码

 

  1.调用new方式分配

int* pInt = new int;

0000004c  mov         ecx4

00000051  call        dword ptr ds:[03B51554h]

qG7Linux联盟
  可以看到,和以前在vc6中一样,分配内存的步骤如下:

  1 首先把sizeof(int) = 4 放到ecx

  2 调用operator new 去分配4个字节

  3 调用构造函数等等......(这里不是我们的重点)

qG7Linux联盟
  成功分配后,会把返回地址放在eax中。

 

  2.调用gcnew方式分配

    int^ rInt = gcnew int;

0000005d  mov         ecx788EF9D8h

00000062  call        FCFAF66C

。。。

    Foo^ rFoo = gcnew Foo;

00000072  mov         ecx3B51768h

00000077  call        FCFAF66C

qG7Linux联盟
  可以看到gcnew也是通过把一个参数放到ecx中,然后再调用一个函数来完成分配的操作,显然0x788EF9D8应该是一个地 址,而不可能是一个数值。我们可以看到这里gcnew创建两个不同类型的变量,调用的函数地址却都是0xFCFAF66C,而存放到ecx中的两个地址就 不一样。究竟这几个地址代表什么呢?

 

  和new一样gcnew也是把返回地址放在eax中。我们直接从内存窗口看eax指向的内存块好了。Aha,看到了没有?

qG7Linux联盟
  这次的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(),只不过这个成员函数也是调用的下面的函数

qG7Linux联盟
OBJECTREF AllocateObject( MethodTable *pMT )

OBJECTREF AllocateObjectSpecial( MethodTable *pMT )

OBJECTREF FastAllocateObject( MethodTable *pMT )

 

  其中AllocateObject又是调用AllocateObjectSpecial来完成工作。那么我们调用的应该就是AllocateObject或者FastAllocateObject了。

qG7Linux联盟
  在我们的例子里面两个call的地址都一样,但是你如果写下代码 double ^ pDouble = gcnew double;这个时候的地址是多少?它和int 的一样么?

qG7Linux联盟
  目前我还没有仔细去研究这个地址到底对应的是该类型的MethodTable::Allocate()或是上面的这三个全局函数, 如果对应MethodTable::Allocate(),那么2.0中应该有个MethodTable::FastAllocate()吧,否则应该就 是对应的全局函数AllocateObject 以及FastAllocateObject了。过几天一定要抽空再好好研究一下。

 

  下面看对应的delete函数

    delete pInt;

000000d8  mov         ecxdword ptr [esp+18h]

000000dc  call        dword ptr ds:[03B51540h]

 

比较简单,就是传入地址,然后调用operator delete来释放类存,会调用析构函数

 

  对应的,释放gcnew创建的对象的代码如下:

    delete rInt;

000000ac  mov         edxedi

000000ae  mov         ecx788F747Ch

000000b3  call        FC8D20FD

qG7Linux联盟
  这个也相对简单,它对应vm里面的一个函数:

void  CallFinalizer(Thread* FinalizerThread Object* fobj)

qG7Linux联盟
  那么也就是

fobjà edx

FinalizerThread à ecx

Call CallFinalizer

 

  但是,请注意!!!!!!!一个类包含析构函数和不包含析构函数,它对应的delete代码是不一样的,这点可以通过汇编代码比较得到,我这里就不多说了。

 

 

 

Linux联盟收集整理 ,转贴请标明原始链接,如有任何疑问欢迎来本站Linux论坛讨论
评论】【加入收藏夹】【 】【打印】【关闭
※ 相关链接
无相关信息