Reputation: 6592
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 ?
}
In the last assignment where is the managed object stored ? I am totally new to C++ CLI. Also, is it true that value types should use stack semantics to make code efficient ? i.e instead of ValBase^ ValBase1 = gcnew ValBase, I should just use ValBase ValBase1;
Upvotes: 0
Views: 1759
Reputation: 27864
As for your second question: Yes, you should remove the ^
when you use value types in C++/CLI. I don't know that there's much of an efficiency gain, but that is the standard for value types.
ValBase valBase1;
is the C++/CLI equivilent to the C# code ValBase valBase1 = new ValBase();
. There is no C# equivalent to the C++/CLI code ValBase^ valBase1
. If you use the ^
on value types, you'll find that you have problems calling .NET APIs, as ValBase^
and ValBase
are different types.
If you need to call a non-default constructor on a value type, here's the syntax. Since there's no heap allocation (either managed or unmanaged), there's no new
or gcnew
, just call the constructor directly.
ValueTypeFoo foo = ValueTypeFoo("bar", "baz");
You can also remove the ^
on reference types, and that will compile to a try-finally-dispose block. Example:
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();
}
Upvotes: 2
Reputation: 941337
Just add a member of a reference type to the value type:
value class ValBase
{
public:
String^ s;
int a;
};
...
ValBase* = new ValBase;
And the compiler tells you exactly where it is stored:
error C3255: 'ValBase' : cannot dynamically allocate this value type object on native heap
The semantics are simple enough, after all you can store a value type on the stack. If it can go on the stack then it can go on the native heap as well. As long as it doesn't contain objects that need to be found back by the garbage collector anyway. Which is why C3255 is there. Value types exist in the .NET framework for this very reason, storing stuff on the stack is cheap and makes code efficient.
But just because it is possible to store it on the native heap doesn't make it exactly useful to do so. Same is true for ValBase^ ValBase1 = gcnew ValBase;
, that stores a boxed value on the managed heap. A copy of the value embedded in a System::Object. Boxing is very useful because it allows pretending that value types inherit from Object. But it isn't cheap, never something you'd want to do for no good reason.
Upvotes: 1