value class ValBase
{
public:
int a;
};
ref class RefBase
{
public:
int a;
};
int main(array<System::String ^> ^args)
{
RefBase^ RefBase1 = gcnew RefBase; //LEGAL. Ref type Managed Obj created on CLR heap.
ValBase^ ValBase1 = gcnew ValBase; //LEGAL. Value type Managed Obj created on CLR heap.
RefBase* RefBase2 = new RefBase; //ILLEGAL: new cannot be used on Managed Ref Class
ValBase* ValBase2 = new ValBase; //This compiles okay but where is this "Managed Object" stored ? CLR heap or Native heap ?
}
在最后一次分配中,托管对象存储在哪里?我是C ++ CLI的新手。此外,值类型是否应该使用堆栈语义来使代码高效?即代替ValBase ^ ValBase1 = gcnew ValBase,我应该使用ValBase ValBase1;
答案 0 :(得分:2)
至于你的第二个问题:是的,你应该在C ++ / CLI中使用值类型时删除^
。我不知道有很多效率提升,但这是价值类型的标准。
ValBase valBase1;
是与C#代码ValBase valBase1 = new ValBase();
无关的C ++ / CLI。没有C#等同于C ++ / CLI代码ValBase^ valBase1
。如果您在值类型上使用^
,则会发现您在调用.NET API时遇到问题,因为ValBase^
和ValBase
是不同的类型。
如果需要在值类型上调用非默认构造函数,这是语法。由于没有堆分配(托管或非托管),没有new
或gcnew
,只需直接调用构造函数。
ValueTypeFoo foo = ValueTypeFoo("bar", "baz");
您还可以删除引用类型上的^
,这将编译为try-finally-dispose块。例如:
StringBuilder sb;
sb.Append("foo");
return sb.ToString();
// Equivalent to:
StringBuilder^ sb = nullptr;
try
{
sb = gcnew StringBuilder();
sb->Append("foo");
return sb->ToString();
}
finally
{
IDisposable^ disp = dynamic_cast<IDisposable^>(sb);
if(disp != nullptr) disp->Dispose();
}
答案 1 :(得分:1)
只需将引用类型的成员添加到值类型:
value class ValBase
{
public:
String^ s;
int a;
};
...
ValBase* = new ValBase;
编译器会告诉您存储的确切位置:
错误C3255:&#39; ValBase&#39; :无法在本机堆上动态分配此值类型对象
语义很简单,毕竟你可以在堆栈上存储一个值类型。如果它可以进入堆栈,那么它也可以在本机堆上运行。只要它不包含垃圾收集器需要找回的对象。这就是C3255在那里的原因。由于这个原因,.NET框架中存在值类型,在堆栈上存储东西很便宜并且使代码有效。
但仅仅因为可能将它存储在本机堆上并不能使它完全有用这样做。 ValBase^ ValBase1 = gcnew ValBase;
也是如此,它在托管堆上存储盒装值。 System :: Object中嵌入的值的副本。 Boxing非常有用,因为它允许假装值类型继承自Object。但它并不便宜,从来没有你想做的事情没有充分的理由。