|
C++ Q&A 专栏... 原代码下载:CQA0412.exe (235KB)
Bernie Sanders
是的,在托管 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 并且系统终止代码调用垃圾收集器释放逗留对象。 ; delete pmc; ldloc.0 ; _pmc$ call ??1ManagedClass@@$$FQ$AAM@XZ 奇怪神秘的符号是被修饰过的析构函数(__dtor)。 struct blah {
int a, b;
char *a, *b;
struct blah *next;
};
struct blah *getmystruct();
因为 getmystruct() 分配内存,当用完之后,我需要调用 freemystruct(struct blah *b)。我尝试做一个包装器,用它来将之转换成托管类型的集合,但我不知道当需要释放所有这些指针的时候,该如何来处理。你能否赐教?
Cory Nelson
为什么,的确。你不能用 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 函数自动完成。 mn->str = nn->str; // String = char*: it just works! 不需要调用转换函数;编译器知道该怎么做。离开 CreateList 时删除本地节点。这样做比在末尾删除它们存储更有效。 String* s = new String();
s = _T("Hello, world");
但我如何才能将一个托管 String 转换回本地的 TCHAR*?
Matthew Brady
一旦你知道了这个神奇的方法,它便很简单。你必须调用 PtrToStringChars
并对结果进行 pin (销连接)操作。代码可以这样你写:String __gc* s = S"Hello"; const wchar_t __pin* p = PtrToStringChars(s); 不要忘了对 PtrToStringChars 返回的指针进行 __pin 操作。销连接是必不可少的,因为 PtrToStringChars
返回指向托管内存中 String 对象第一个字符的托管(__gc)指针,垃圾收集器可能在任何高兴的时候移走托管内存,除非你显示地对之进行 __pin
操作。一般来讲,你必须在将 __gc 指针传递给某个本地(非托管)函数的任何时候使用用 __pin。 // both will work CString s1 = "hello, world"; CString s2 = L"Hello, world";
Mayur Patel
改变标签控件中标签的颜色十分简单,但要想让属性页充满某种颜色,这个改造涉及相当大的工作量,对于一个胆小的人来说,是不敢轻举妄动的。对于标签来说,基本思路让该控件是自绘控件,然后处理 WM_DRAWITEM
消息。如果使用 MFC,你可以改写虚拟函数 DrawItem。在 1998 年三月坎的 Microsoft Systems Journal 中,我示范了如何实现一个标签控件类 CTabCtrlWithDisable,这个类支持标签禁用。作为禁用标签的一部份,当标签被禁用时, CTabCtrlWithDisable 将标签文本颜色改成了浅灰色,本文我借用了 CTabCtrlWithDisable 中的一些代码实现了一个新类 CColorTablCtrl,使你能改变标签的颜色。(参见 Figure 5) 为了使用 CColorTablCtrl, 在你的属性页中创建一个实例: class CMyPropSheet : public CPropertySheet {
protected:
CColorTabCtrl m_tabCtrl;
};
你必须在属性页的 OnInitDialog 处理例程(这样 MFC 将使用它)中子类化标签控件,然后按照自己的意愿设置前景色和背景色: // in CMyPropSheet::OnInitDialog() HWND hWndTab = (HWND)SendMessage(PSM_GETTABCONTROL); m_tabCtrl.SubclassDlgItem(::GetDlgCtrlID(hWndTab), this); m_tabCtrl.SetColor(WHITE, RED); 这里 WHITE 和 RED 是标准的 COLORREF 值,也就是 RGB(255, 255, 255) 和
RGB(255,0,0)。一旦你实例化并初始化 CColorTabCtrl,颜色标签控件便负责其余的事情。(参见 Figure 6) // in CColorTabCtrl::DrawItem dc.FillSolidRect(rc, m_clrBackground); dc.SetBkColor(m_clrBackground); dc.SetTextColor(m_clrForeground); dc.DrawText(...); 就这么简单直白,具体细节请看源代码。由于你可能在页面颜色没有改变的情况下也不想改变标签颜色,所以我还实现了一个 CColorPropertyPage 类,使你能改变属性页的背景色以便和标签颜色匹配,如果 Figure 6 所示,对于属性页而言,改变背景色最容易的方法是处理 WM_ERASEBKGND: BOOL CColorPropertyPage::OnEraseBkgnd(CDC* pDC)
{
CRect rc;
GetClientRect(&rc);
pDC->FillSolidRect(rc, m_clrBackground);
return TRUE;
}
如果你只是尝试,你会发现各种恼人的问题。首先,如果你改变页面颜色,所有控件的背景色都是错误的,所以你必须还要解决这个问题。为此,你不得不处理处理 WM_CTLCOLOR
和 WM_ERASEBKGND。具体细节请参考我在 MSJ May 1997 专栏的文章。另外一个问题是标签控件仍然以系统3D颜色绘制出边缘和四个角。这样肯定不行,为解决这个问题,除了处理 WM_PAINT 消息别无选择,并且要自己负责整个的绘制操作。它包括绘制选中时标签之间的偏移,以便使它看起来是在前面。至此,可以说你已开始重新发明标签控件,每一个使用 Windows 的程序员都知道,改变控件的颜色最痛苦的一件事情,并且一旦你开始走上这条路,便会觉得要做完它真是遥遥无期。不久标准的颜色看起来将比起初的颜色更好,或者你将会觉得为什么不转到 .NET 框架上来,在那上面改变颜色实在简单,用如下代码即可: ctl.BackColor = Color.Aquamarine; 编程愉快! |
作者简介Paul DiLascia 是一名自由作家,顾问和 Web/UI 设计者。他是《Writing Reusable Windows Code in C++》书(Addison-Wesley, 1992)的作者。通过 http://www.dilascia.com 可以获得更多了解。 |
| 本文出自 MSDN Magazine 的 December 2004 期刊,可通过当地报摊获得,或者最好是 订阅 |
背景:
阅读新闻
删除托管对象,如何果包装一个库?及其它......
| [日期:2005-12-30] | 作者: | [字体:大 中 小] |
阅读: 次
【 打印 】
【 打印 】
相关新闻
相关关键词:
全站导航

是的,在托管 C++ 中,你可以删除( delete
)托管对象,只要你理解删除只不过是调用对象的析构函数,但析构函数必须显示定义。调用 delete
不会释放对象的存储区。只有垃圾收集器才行。

作者简介
gmail.com