如何删除托管对象及包装一个库

在托管 C++ 中,请告诉我使用 delete 操作符销毁托管对象是否安全?

是的,在托管 C++ 中,你可以删除( delete )托管对象,只要你理解删除只不过是调用对象的析构函数,但析构函数必须显示定义。调用 delete 不会释放对象的存储区。只有垃圾收集器才行。Figure 1 展示了一个简单的程序,该程序定义了一个带析构函数的托管类,当它运行的时候会显示一条信息。TESTDTOR 分配两个 ManagedClass 实例。它显式删除第一个实例,但第二个则不然。如果运行 TESTDTOR,你会得到象下面这样的结果:

Begin main
ManagedClass(04A712D4)::ctor
ManagedClass(04A712D4)::dtor
ManagedClass(04A712E0)::ctor
End main
ManagedClass(04A712E0)::dtor

它说明了当 delete 语句执行时,第一个对象的析构函数立即执行;而第二个对象(at 04A712E0)则没有被销毁,直到控制离开 main 并且系统终止代码调用垃圾收集器释放逗留对象。

Figure 2 Testdtor 的精彩输出

不管什么时候,如果你不能确定 .NET 环境中发生了什么,你总是可以编写一些代码,编译它并检查微软中间语言(MSIL)产生的东西。正如 Figure 2 所展示的,定义析构函数导致编译器产生两个方法:一个是 Finalize 方法,它包含你的实现(这里是调用 printf),一个是 __dtor 方法,它调用 System.GC::SuppressFinalize,然后再调用 Finalize。当你删除对象时,编译器产生一个对此 __dtor 方法的掉用。如果你用 /FAs 编译 TESTDTOR 来产生有源码的程序集清单,你将看到 delete 语句以如下的方式编译:

; delete pmc;ldloc.0 ; _pmc$call ??1ManagedClass@@$$FQ$AAM@XZ

老练的 C++ 程序员可能会弄不明白,如果调用 delete 都无法释放对象,那调用它有干什么?好问题。调用 delete 的唯一理由是收回任何你的类所使用的非托管资源。例如,如果你的对象打开数个文件或创建了数据库连接,你可以写一个关闭其资源的析构函数,然后在用完该对象时使用 delete 释放它。在托管类中释放资源的一个更好的方法是通过实现 Dispose 模式,IDisposable——如果你在写托管 C++ 代码——由 auto_dispose 来调用它。(更多的信息参见 Tomas Restrepo 在 MSDN 杂志 2002 二月刊上的文章:“Tips and Tricks to Bolster Your Managed C++ Code in Visual Studio .NET”)。

如果你实现 dispose 模式,其他的 .NET 使用者也可以使用它。如果你自己在析构函数中进行清理,其它语言便没有办法显式调用你的清理代码。因为在 C# 和 Visual Basic 中没有 delete 操作符。

所以结果是你能调用 delete 来触发你的析构函数,但是将清理代码放在析构函数中可能不是一个好主意。最好是实现 IDisposable,这样所有人都能使用。注意,在 Visual C++ 2005 中,这个行为有所改变。更多信息参见 Andy Rich 对这个问题的讨论:“Deterministic Finalization IV - Benefits, part II”,以及当前的 C++/CLI 语言规范标准:“C++/CLI Language Specification Standard”

我有一个返回链表的非托管函数,其中有 char* 字符串:

struct blah {   int a, b;   char *a, *b;   struct blah *next;};struct blah *getmystruct();

因为 getmystruct() 分配内存,当用完之后,我需要调用 freemystruct(struct blah *b)。我尝试做一个包装器,用它来将之转换成托管类型的集合,但我不知道当需要释放所有这些指针的时候,该如何来处理。你能否赐教?

为什么,的确。你不能用 dllimport 语句将你的本地链表转换成托管类型集合。interop 服务固然不错,但处理此问题也不是那么好!你需要编写一个包装器,显式地将你的链表转换为托管集合,象 ArrayList。我写了一个带有三个模块的程序,ListWrap,它示范了具体做法。第一个模块,ListLib.cpp,实现一个本地 C++ 库(DLL),其中有两个函数,AllocateList 和 FreeList。分别用来分配和释放本地 C++ 结构链表。它们模仿你程序中的 getmystruct 和 freemystruct 函数。第二个模块是一个托管 C++ 文件,ListWrap.cpp,它实现托管类 ManagedNode,该类包装本地 C++ 实现(参见 Figure 3)。第三个模块是 C# 测试程序,它调用包装器来展示它如何工作。详情请下载源代码。

ListLib.cpp 实现两个本地函数,AllocateList 和 FreeList,这两个函数用来分配和释放 NativeNode 结构链表:

// from ListLib.h
struct NativeNode {
  int a, b;
  TCHAR *str;
  struct NativeNode *next;
};

ListWrap.cpp 中的包装器类 ManagedNode 模仿用 NativeNode 的定义,只是有两个小差别:本地 char* 被用托管的 String 代替,此外它没有 next 指针,因为我将用 ArrayList 实现链表结构。代码如下:

// managed equivalent of NativeNode
public __gc class ManagedNode {
public:
int a, b;
String* str;
};

有了 ManagedNode 的定义,下一步是编写代码将 NativeNodes 转换到 ManagedNodes。但在开始之前,先停下来考虑一下转换函数应该是什么样子,他应该有什么样的参数,返回什么值。一种方法是编写一个函数,参数是本地 NativeNodes 链表并返回托管的 ManagedNodes 链表,在这个过程中可能销毁本地链表。.NET 客户端应用程序将直接调用 ListLib DLL (或你的 getmystruct )以获取本地链表,将它作为 IntPtr 类型。然后,将这个 IntPtr 传递给转换函数,象下面这样:

// call DLL directly through interop
IntPtr nativeList = AllocateList(7);
// call wrapper to convert
ArrayList amanagedList = ListWrap.Convert(nativeList);

大多数情况下,客户端将负责调用该 DLL 来释放本地链表,或者 Convert 函数自动完成。

一种不同的方法是通过在某个包装器中包装分配链表的本地函数 AllocateList 来完全隐藏这个 DLL,转换并在作为 ArrayList 返回托管链表之前释放原来的本地链表。哪种方法更好的呢?第一种策略的优点是你只需要编写单一的转换函数,它便可以在任何有本地链表的地方使用。第二个方法需要对每个创建链表的函数进行包装。如果有多个创建链表的函数,则工作量稍大一些。但是其优点是它向 .NET 客户端完全隐藏了所有的本地处理逻辑和细节。客户端不再需要去处理 IntPtrs 或甚至是导入此 DLL,因为 ListWrap 隐藏了一切。这是我将要采用的方法,同时也是我鼓励你在自己的程序中使用的方法。尽管对库进行完全的包装需要更多的努力,但是结果却更加可靠和彻底的封装。

有了 ManagedNode,剩下的事情便是包装 AllocateList。这个过程非常简单直白。首先,调用 AllocateList 分配本地链表,然后创建一个空的 ArrayList,接着将所有 NativeNodes 拷贝到 ManagedNodes 并将它们添加到托管链表中,最有离开时删除它们。Figure 3 展示了所有的细节。托管 C++ 的优美之处在于即便是在处理混合对象时,所有的代码看起来都很简朴优雅。将本地 char* 拷贝到托管 String 用一个赋值即可,就像下面这行代码:

mn->str = nn->str; // String = char*: it just works!

不需要调用转换函数;编译器知道该怎么做。离开 CreateList 时删除本地节点。这样做比在末尾删除它们存储更有效。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索c++
, 对象
, 函数
, c库转换c#可用库
, 本地
, 托管
, class struct 链表
, c++类 链表
, 文件操作c++链表
, c++链表
, c++ 数据结构 链表 c
, 一个
, C++中的delete
C++信息隐藏类型
托管对象、非托管对象、托管dll包装、托管模式无法删除、处于托管模式无法删除,以便于您获取更多的相关知识。

时间: 2024-08-01 07:16:06

如何删除托管对象及包装一个库的相关文章

Win7系统怎么删除右键新建中有一个Skin Builder Project?

  Win7系统怎么删除右键新建中有一个Skin Builder Project?           在win7系统中我们要新建一个文件,只要鼠标右键点击新建,我们就可以创建一个格式的新文件,不过有朋友反馈新建中有一个Skin Builder Project是什么呢?如何删除?不要着急,下面小编就教大家怎么删除. 通过分析这是由于安装了skin builder然后卸载不干净而造成 解决方法: 1.按下Windows功能键+R,运行regedit. 2.展开HKEY_CLASSES_ROOT 你

请问 mysqldump 备份的sql文件 导入到另一个库中,为什么老是少数据?

问题描述 请问 mysqldump 备份的sql文件 导入到另一个库中,为什么老是少数据? 直接导出的database 请问 mysqldump 备份的sql文件 导入到另一个库中,为什么老是少数据? 备份时就加了 --events 这一个参数. 求指导~ 解决方案 是不是导出了所有对应的database,可以直接查看导出的sql文件中是否含有这些表

sqlalchemy-mysql 从一个库 DA 选择表 TA 内容,插入到另一个 DB 库的 TB 中

问题描述 mysql 从一个库 DA 选择表 TA 内容,插入到另一个 DB 库的 TB 中 如果两个库是一台物理机大概~ INSERT INTO DB.TB SELECT * FROM DA.TA WHERE .... 但是如果是两个库分别属于两个物理机~~ 我使用 sqlalchemy~ 第一段 fet = sesssionA.execute('SELECT * FROM TA WHERE ...').fetchall() 第二段 ''' 根据 fet 构造成 dict''' 第三段 ses

用做产品的思路去包装一个艺人

摘要: 尚雯婕跨界电商 谁也不会想到,尚雯婕会成为一个励志故事. 冠军光环转瞬即逝,接下来是长达几年的焦虑期:市场定位不准,出唱片不赚钱.高额投资收益惨淡,尚雯婕几乎要被华 尚雯婕跨界电商 谁也不会想到,尚雯婕会成为一个励志故事. 冠军光环转瞬即逝,接下来是长达几年的焦虑期:市场定位不准,出唱片不赚钱.高额投资收益惨淡,尚雯婕几乎要被华谊放弃.最惨时,尚雯婕给华谊负责商务.后来成为8年合伙人的聂心远打电话,两个人边聊边哭. 早期围绕尚雯婕的全是过于夸张的造型带来的质疑.但是几年过去,尚雯婕不一样

如何判断Unix系统的一个库文件是32位还是64位的

如何判断Unix系统的一个库文件是32位还是64位的 某些时候,我们需要知道操作系统的位数,或者配置插件的时候需要知道主程序的位数(例如配置apache插件的时候需要知道apache的位数以便配置相应的插件),最简单的办法就是执行file命令,如: file 命令 Linux: # file libnss1_files-2.2.4.so  libnss1_files-2.2.4.so: ELF 32-bit LSB shared object, Intel 80386, version 1, n

mysql单表体积和一个库设计多少张表为妥

这篇文章来自于看博客园一个园友的分享经历,原文:http://www.cnblogs.com/qqloving/p/3427138.html   他不清楚mysql一个库里面分多少张表合适,他一个库分了8000张表.于是我看了,忍不住作答.   于是以个人随笔的形式给自己做知识备忘吧.   1.单表体积多大的时候需要分表   曾经看过一个博客,分析到什么情况下需要分表. 单表形式访问(也就是对这个表的访问不涉及到join联合查询):单个表的体积大于2g的时候.或者说,单个表的行数达到一千万的时候

Python程序员都该用的一个库

本文的作者来自知名 Python 库 Twisted 开发团队,首先举例说明了在 Python 中定义类是多么的麻烦,然后给出了自己的解决方案:attrs 库.从介绍来看,确实方便很多. 你写 Python 程序吗?那你应该使用 attrs. 你问为什么?我只能说,不要问,直接用就好了. 好吧,我还是解释一下. 我热爱 Python,这十多年来一直是我的主力编程语言.尽管期间也出现过一些有意思的语言(指的是 Haskell 和 Rust),但我还不打算换到其他语言. 这不是说 Python 没有

删除-关于SQL的一个小问题 我有一个Data表 里面许多个字段 请看下面的图片

问题描述 关于SQL的一个小问题 我有一个Data表 里面许多个字段 请看下面的图片 关于SQL的一个小问题 我有一个Data表 里面许多个字段 我要删除图中DataID=24的 D_Dac Time 里面的内容 其他都不要删 sql语句怎么写? 解决方案 delete D_Dac Time from 表名 where dataID=24; 解决方案二: update xxx set xxx=null where id=xxx 解决方案三: 如果要删除DataID=24中一行全部内容又该怎么写呢

MySQL命令行删除表中的一个字段_Mysql

先看看删除之前的表结构: mysql> select * from test; +------+--------+----------------------------------+------------+------------+------------+------------+ | t_id | t_name | t_password                       | t_birth    | birth      | birth1     | birth2     |