通用托管 C++陷阱
在几乎完全使用非托管 C++ 后首次编写托管 C++ 时,需要注意哪些最常见的问题?
What are some of the most common issues to look out for when writing managed-C++ for the first time after almost exclusively working with unmanaged C++?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
当您使用内置 IJW(It Just Works)技术将托管委托转换为函数指针时,函数指针不保存对委托所属对象的引用。如果您不安排以其他方式保存引用,则可能会收集托管对象,然后当您调用该函数时,您将得到 NullReferenceException。
如果您正在创建一个需要回调的 C 库,并且希望将其包装在托管类中,那么您会经常遇到这种情况。托管类的客户端将提供一个委托,您可以将其转换为回调。您还必须保留对委托或目标对象的引用。
这在托管 C++ 和 C++/CLI 中是正确的。
When you convert a managed delegate to a pointer-to-function using the built-in IJW (It Just Works) technology, the pointer-to-function is not holding a reference to the object that the delegate was from. If you don't arrange to hold a reference some other way, the managed object may be collected, and then when you call the function, you'll get a NullReferenceException.
You run into this a lot if you are making a C-library that takes callbacks, and you want to wrap it in a Managed class. The client to the managed class will provide a delegate, which you convert to the callback. You must also keep a reference to the delegate or target object.
This is true in Managed C++ and C++/CLI.
如果您指的是 C++/CLI...
yield
构造,我们在使用 Nunit 编写单元测试时经常使用它,使用 TestCaseSource 属性生成测试用例数据。这与下一篇有关。If you mean C++/CLI...
yield
construct, which we use a lot when writing Unit Tests with Nunit using the TestCaseSource attribute to generate test case data. This is related to the next one.这是另一个问题:在对象上执行方法不构成对该对象的引用。这意味着在执行成员方法期间,在您最后一次引用
this
之后,该对象可能会在该方法仍在执行时被清理,并且终结器可能会触发。如果您的对象具有通过终结清除的任何非托管状态,或者包含具有此类非托管状态的任何其他对象,那么您正在对该非托管状态进行计算,请务必在
GC::KeepAlive
上调用GC::KeepAlive
在那些非托管计算之后的 code>this 。我现在倾向于将 GC::KeepAlive 附加到具有非托管计算的对象的所有方法。Here's another gotcha: Executing a method on an object does not constitute a reference to the object. This means that during execution of a member method the object but after your last reference to
this
the object might be cleaned up while the method is still executing and the finalizer may fire.If your object has any unmanaged state that is cleaned up by finalization or contains any other object with such unmanaged state are you're doing computations on that unmanaged state, be sure to call
GC::KeepAlive
onthis
after those unmanaged computations. I now tend to just appendGC::KeepAlive
to all methods of objects with unmanaged computation.一个陷阱(刚刚让我困惑):假设“析构函数只能执行一次”。由于析构函数是通过
Dispose
调用的,并且Dispose
可能会被多次调用,因此这个假设(对于 C++ 来说没问题)不适用于 C++/CLI。One gotcha (that just got me): assuming "the destructor can only be executed once". Since the destructor is invoked via
Dispose
, andDispose
might be called multiple times, this assumption (which is fine for C++) does not hold for C++/CLI.