我正在使用这个课程:
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
答案 0 :(得分:7)
这是gcroot&lt;&gt;的错误用法,如果您在非托管类中保留对托管对象的引用,则只需要这样做。
在这种情况下,您只需要声明它string*
。一个指针,你不能在你的托管类中存储一个字符串对象,编译器确信你要射击你的腿。你会得到你现在得到的完全相同的错误信息。
真的很糟糕当垃圾收集器压缩gc堆并移动Form对象时,就会发生这种情况。这使得对字符串对象的任何外部非托管指针无效,而垃圾收集器无法更新该指针。例如,当您将对字符串的引用传递给非托管代码并且在执行非托管代码时发生GC时,可以生成这样的指针。指针现在不再有效,并且非托管代码在读取垃圾或破坏GC堆时失败。特别是后一种事故很难诊断出来。只需调用其中一个std :: string成员方法就足以调用此失败模式,从而生成 this 指针。
在托管对象中实际需要一个std :: string是非常不寻常的,您总是先选择String^
。并且仅在需要时生成std :: string,通常是在调用本机代码时。如果在运行中创建std :: string非常昂贵,那么只考虑按照自己的方式进行操作。如果您这样做,那么在构造函数中创建std :: string对象。并在析构函数和终结器中再次销毁它。这对于Form类来说是非常棘手的,因为它已经有一个析构函数和终结器,强烈考虑创建一个存储字符串的小助手类。
答案 1 :(得分:4)
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 Forms GUI非常困难 - 您需要了解两者C ++和.NET。在任何情况下,C ++ / CLI Windows窗体应用程序类型都被放弃在最新的Visual Studio版本中。重新考虑你的方法。
答案 2 :(得分:1)
如果在托管类中嵌入非托管类,则需要“固定”它,因为垃圾回收器在执行托管类时实际上可能“紧凑”,实质上是在堆中移动非托管类的实际地址这可能会导致巨大的问题。因此,在初始化包含任何非托管引用的托管类时,请使用__pin
关键字。
ManagedClassWithEmbeddedUnmanagedClass __pin *pMC = new ManagedClassWithEmbeddedUnmanagedClass();