这个托管对象存储在哪里?

时间:2012-01-18 23:25:30

标签: c++-cli

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;

2 个答案:

答案 0 :(得分:2)

至于你的第二个问题:是的,你应该在C ++ / CLI中使用值类型时删除^。我不知道有很多效率提升,但这是价值类型的标准。

ValBase valBase1;是与C#代码ValBase valBase1 = new ValBase();无关的C ++ / CLI。没有C#等同于C ++ / CLI代码ValBase^ valBase1。如果您在值类型上使用^,则会发现您在调用.NET API时遇到问题,因为ValBase^ValBase是不同的类型。

如果需要在值类型上调用非默认构造函数,这是语法。由于没有堆分配(托管或非托管),没有newgcnew,只需直接调用构造函数。

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。但它并不便宜,从来没有你想做的事情没有充分的理由。