C++ 具有非托管成员的托管类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20173189/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Managed class with a non-managed member
提问by HalaKuwatly
I'm using this class:
我正在使用这个类:
public ref class x: public System::Windows::Forms::Form
{
private: gcroot<std::string> name;
}
and i am getting the following error:
我收到以下错误:
IntelliSense: a member of a managed class cannot be of a non-managed class type
I know i can use char*
, but if I use lots of char*
i will have to manually do the delete[]
or some heap corruption issues will rise
I've been stuck on this for two days now
note: I have to use c++ and have to use UI in c++
我知道我可以使用char*
,但是如果我使用很多,char*
我将不得不手动执行,delete[]
否则会出现一些堆损坏问题
我已经坚持了两天了
注意:我必须使用 c++ 并且必须在C++
回答by Hans Passant
That's the wrong usage for gcroot<>, you only need that if you keep a reference to a managedobject in an unmanaged class.
这是 gcroot<> 的错误用法,只有在非托管类中保留对托管对象的引用时才需要这样做。
In this case you only need to declare it string*
. A pointer, you cannot store an string object inside your managed class, the compiler is convinced that you are going to shoot your leg off. You'll get the exact same error message you got now.
在这种情况下,您只需要声明它string*
。一个指针,你不能在你的托管类中存储一个字符串对象,编译器确信你会开枪。您将收到与现在完全相同的错误消息。
Really Bad Things can happen when the garbage collector compacts the gc heap and moves the Form object. That invalidates any external unmanaged pointer to the string object without the garbage collector being able to update that pointer. Such a pointer can for example be generated when you pass a reference to the string to unmanaged code and the GC happens while that unmanaged code is executing. The pointer is now no longer valid and the unmanaged code fails when it reads garbage or corrupts the GC heap. Especially the latter mishap is extremely hard to diagnose. Just calling one of the std::string member methods is enough to invoke this failure mode, that generates the thispointer.
当垃圾收集器压缩 gc 堆并移动 Form 对象时,可能会发生非常糟糕的事情。这会使指向字符串对象的任何外部非托管指针无效,而垃圾收集器无法更新该指针。例如,当您将对该字符串的引用传递给非托管代码并且在该非托管代码执行时发生 GC 时,可以生成这样的指针。当非托管代码读取垃圾或破坏 GC 堆时,该指针不再有效并且非托管代码失败。尤其是后一种事故,极难诊断。只需调用 std::string 成员方法之一就足以调用这种生成this指针的故障模式。
It is very unusual to actually needan std::string in a managed object, you always choose String^
first. And generate the std::string only when needed, typically when you call native code. Only ever consider doing it your way if creating the std::string on-the-fly is exceedingly expensive. If you do then create the std::string object in the constructor. And destroy it again in both the destructor and the finalizer. Which is pretty tricky to get right for the Form class since it already has a destructor and finalizer, strongly consider creating a little helper class that stores the string.
在托管对象中实际需要std::string是非常不寻常的,您总是String^
首先选择。并且仅在需要时生成 std::string,通常是在您调用本机代码时。如果即时创建 std::string 非常昂贵,请只考虑按照自己的方式进行操作。如果这样做,则在构造函数中创建 std::string 对象。并在析构函数和终结器中再次销毁它。这对于 Form 类来说非常棘手,因为它已经有一个析构函数和终结器,强烈考虑创建一个存储字符串的小助手类。
回答by Alex F
gcroot
is used for another purpose: to keep CLR class member in a native class. You need to keep native class member in CLR class. This is not allowed directly as well, but you can keep pointer to native class as a member:
gcroot
用于另一个目的:将 CLR 类成员保留在本机类中。您需要在 CLR 类中保留本机类成员。这也不允许直接使用,但您可以将指向本机类的指针保留为成员:
private: std::string* name;
Initialize name
to new string("...");
in the class constructor or in another place where this is necessary, and use it. To prevent memory leak, release name
in the class finalizer and destructor.
在类构造函数或其他需要的地方初始化name
tonew string("...");
并使用它。为防止内存泄漏,请name
在类终结器和析构器中释放。
Notes:
笔记:
Avoid using mixed types if this is not absolutely necessary. For example, in this case you can use pure CLR
String^
instead of nativestd::string
.To write native C++ with GUI it is much better to use some native UI framework: Win32 API, MFC, Qt etc. Using Windows Forms GUI in C++/CLI is very difficult way - you need to know well both C++ and .NET. In any case, C++/CLI Windows Forms application type is abandoned in newest Visual Studio versions. Reconsider your approach.
如果这不是绝对必要的,请避免使用混合类型。例如,在这种情况下,您可以使用纯 CLR
String^
而不是 nativestd::string
。要使用 GUI 编写本机 C++,最好使用一些本机 UI 框架:Win32 API、MFC、Qt 等。在 C++/CLI 中使用 Windows 窗体 GUI 是非常困难的方式 - 您需要对 C++ 和 .NET 都了如指掌。在任何情况下,最新的 Visual Studio 版本都放弃了 C++/CLI Windows 窗体应用程序类型。重新考虑你的方法。
回答by Alex F
If you embed an unmanaged class within a managed class you'll need to 'pin' it because the garbage collector might actually 'compact' during execution of the managed class, essentially moving the actual address of the unmanaged class in the heap which can cause huge problems. So use the __pin
keyword when initializing your managed class that contains any unmanaged references.
如果在托管类中嵌入非托管类,则需要“固定”它,因为垃圾收集器实际上可能会在托管类执行期间“压缩”,实质上是在堆中移动非托管类的实际地址,这可能导致巨大的问题。因此,__pin
在初始化包含任何非托管引用的托管类时使用关键字。
ManagedClassWithEmbeddedUnmanagedClass __pin *pMC = new ManagedClassWithEmbeddedUnmanagedClass();