具有非托管成员的托管类

Posted

技术标签:

【中文标题】具有非托管成员的托管类【英文标题】:Managed class with a non-managed member 【发布时间】:2013-11-24 09:48:38 【问题描述】:

我正在使用这个类:

public ref class x: public System::Windows::Forms::Form
 
     private: gcroot<std::string> name;

我收到以下错误:

IntelliSense: a member of a managed class cannot be of a non-managed class type

我知道我可以使用char*,但如果我使用大量char*,我将不得不手动执行delete[],否则会出现一些堆损坏问题 我已经坚持了两天了 注意:我必须使用 c++ 并且必须在 c++ 中使用 UI

【问题讨论】:

【参考方案1】:

这是 gcroot 的错误用法,只有在非托管类中保留对 托管 对象的引用时,您才需要这样做。

在这种情况下,您只需声明它string*。一个指针,你不能在你的托管类中存储一个字符串对象,编译器确信你会打断你的腿。您将收到与现在完全相同的错误消息。

当垃圾收集器压缩 gc 堆并移动 Form 对象时,可能会发生非常糟糕的事情。这会使指向字符串对象的任何外部非托管指针无效,而垃圾收集器无法更新该指针。例如,当您将对字符串的引用传递给非托管代码并且在该非托管代码执行时发生 GC 时,可以生成这样的指针。指针现在不再有效,并且非托管代码在读取垃圾或损坏 GC 堆时失败。尤其是后一种事故极难诊断。只需调用其中一个 std::string 成员方法就足以调用此故障模式,它会生成 this 指针。

在托管对象中实际上需要 std::string 是非常不寻常的,您总是首先选择String^。并且仅在需要时生成 std​​::string,通常是在您调用本机代码时。只有在即时创建 std::string 非常昂贵时才考虑按照自己的方式进行操作。如果这样做,则在构造函数中创建 std::string 对象。并在析构函数和终结器中再次销毁它。由于 Form 类已经具有析构函数和终结器,因此很难正确处理,因此强烈考虑创建一个存储字符串的小助手类。

【讨论】:

【参考方案2】:

gcroot 用于另一个目的:将 CLR 类成员保留在本机类中。您需要在 CLR 类中保留本机类成员。这也不允许直接使用,但您可以将指向本机类的指针保留为成员:

private: std::string* name;

在类构造函数中或在其他需要这样做的地方将name 初始化为new string("...");,然后使用它。为防止内存泄漏,请在类终结器和析构器中释放name

注意事项:

    如果不是绝对必要,请避免使用混合类型。例如,在这种情况下,您可以使用纯 CLR String^ 而不是原生 std::string

    要使用 GUI 编写本机 C++,最好使用一些本机 UI 框架:Win32 API、MFC、Qt 等。在 C++/CLI 中使用 Windows 窗体 GUI 是非常困难的方法 - 你需要熟悉两者C++ 和 .NET。在任何情况下,最新的 Visual Studio 版本都放弃了 C++/CLI Windows Forms 应用程序类型。重新考虑你的方法。

【讨论】:

释放是指删除? 是的。 delete name; name = NULL;【参考方案3】:

如果您将非托管类嵌入到托管类中,则需要“固定”它,因为垃圾收集器实际上可能在托管类执行期间“压缩”,实质上是在堆中移动非托管类的实际地址这可能会导致巨大的问题。因此,在初始化包含任何非托管引用的托管类时,请使用 __pin 关键字。

ManagedClassWithEmbeddedUnmanagedClass __pin *pMC = new ManagedClassWithEmbeddedUnmanagedClass();

【讨论】:

以上是关于具有非托管成员的托管类的主要内容,如果未能解决你的问题,请参考以下文章

封送包含 char* 成员的非托管结构

未导出成员函数时,从 C# 调用 C++ 本机/非托管成员函数

C# 托管非托管代码

具有非托管属性的 awakeFromFetch

将非托管方法作为回调传递给托管 C++/CLI 类

WCF 服务调用具有复杂数据类型的非托管 c++ dll