1

I know that value classes don't have an default constructor as the compiler initializes all elements in this class with zero. But arrays are in a value class are not initialized:

value class c_LocationVal
{
public:
  double x, y, z;
  c_LocationVal(double i_x, double i_y, double i_z) {x = i_x; y = i_y; z = i_z;}
};

typedef cli::array<c_LocationVal> arrloc;

value class c_Managed
{
public:
  arrloc^ m_alocTest;

  //c_Managed() { m_alocTest = gcnew arrloc(3); }  --> not permitted

  double funcManaged ()
  {
    return m_alocTest[0].x;  --> error: Object reference not set to an instance of an object
  }
};

I just could cheat and use:

c_Managed(int i) { m_alocTest = gcnew arrloc(3); }

but there must be another solution.
Can someone please tell me how to solve this?

Tobias Knauss
  • 3,361
  • 1
  • 21
  • 45
  • What you call cheating is actually required in C#. I assume the same rule goes for C++/CLI. Can't you declare a C array of `c_LocationVal` instead? – leppie Jul 19 '13 at 13:16
  • Unfortunately I can't: I will get the compiler error C2728 when I create a C-array of a managed type, and I will get compiler error C4368 when I create an array of a unmanaged type inside a managed type. And because the main application is a CLR-Winform, I cannot use unmanaged arrays. – Tobias Knauss Jul 19 '13 at 13:50
  • possible duplicate of [How do I specify a fixed-size buffer in C++/CLI?](http://stackoverflow.com/questions/2138473/how-do-i-specify-a-fixed-size-buffer-in-c-cli) – Ben Voigt Jul 23 '13 at 03:02

3 Answers3

2

The CLR only supports code inside of methods. Compilers emulate the behavior of a member initialization expression by creating a constructor, if necessary, and moving the code for the expression into the constructor.

Which explains why this isn't permitted, your expression requires a parameterless constructor and that's not legal for a value type.

Sure, your trick will work. But in general, you need to de-tune C++ assumptions a bit when you write C++/CLI code. There are no practical differences between a struct and a class in C++. But that's definitely not the case in managed code. Only ever use a value class for very simple types. Requiring initialization heavily tips the choice to a ref class. As does a value type having an array, you'd normally need a deep copy to make that work without accidents. Never fear the heap in C++/CLI, it is very fast.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I just thought I could avoid implementing a copy constructor or clone method by making the class a value type. But when you say that value types should only be used for very simple constructs to avoid problems, then this is the wrong type for my application and I really need to deal with a ref class plus clone method now. – Tobias Knauss Jul 19 '13 at 13:58
  • That's another thing you have to de-tune, the rule of 3 is important in C++. It is not in managed code, you just about never write a copy constructor. The garbage collector removes the need. – Hans Passant Jul 19 '13 at 14:02
  • I've now come across the same problem again. I need the functionality of a value class, but it must contain an array of the small value class `c_LocationVal`, so I have to make the superior class a ref class. But that means I need to implement a clone method, thus I cannot 'de-tune' as much as I wanted, or is there another solution to it? – Tobias Knauss Aug 13 '13 at 08:29
  • Yet again something to de-tune. C++ only has value classes, the preferred type in a managed program is a reference type class. Only ever use a value type for very small structs, no more than 16 bytes. There is no built-in support for cloning reference types, you simply add a CopyTo() method to make your own deep copy. – Hans Passant Aug 13 '13 at 08:54
  • Yet again something to de-tune. C++ only has value classes, the preferred type in a managed program is a reference type class. Only ever use a value type for very small structs, no more than 16 bytes. Cloning a reference type object is supported by the inherited Object::MemberwiseClone() but that's a shallow copy and very rarely useful. Simply add a CopyTo() method to make your own deep copy. There's otherwise nothing that stops you from creating a copy constructor as long as only C++/CLI code uses the class. – Hans Passant Aug 13 '13 at 09:20
1

A value class is always initialized with "null/0". So a managed reference in a value class will also always be initialized to "null". If you want to have a special initialization, then you only have the solution, you were pointing out: You need to create a special constructor which has some parameters to "initialize" the value class correctly.

The question is: Do you really need a value class which contains a managed reference??? Normally this should also be a ref class.

Also, what happens, if the value class is copied? What should happen with the reference? It will also directly copied! Is this intended? The goal of a value class is to provide a "real" copy! In your case it will not "fully copied"...

Pleas re-think if a value class is the best solution for your data storage...

Jochen Kalmbach
  • 3,549
  • 17
  • 18
  • So because the array is a reference type, I should not use it in a value class? And the array reference is copied but not the array content? This makes sense. I really should start the weekend now. ;-) – Tobias Knauss Jul 19 '13 at 14:12
  • If he embeds the array directly inside the value class, instead of a reference, it will have the value copy semantic he's looking for. – Ben Voigt Jul 23 '13 at 03:03
0

Switch the public field to a property, and do lazy initialization when the property is retrieved.

value class c_Managed
{
private:
  arrloc^ m_alocTest;

public:
  arrloc^ AlocTest
  {
    arrloc^ get()
    {
      if(m_alocTest == nullptr)
      {
        msclr::lock(c_Managed::typeid)
        if(m_alocTest == nullptr)
          m_alocTest = gcnew arrloc(3);
      }

      return m_alocTest;
    }
  }

  double funcManaged ()
  {
    return AlocTest[0].x;
  }
};

The lock for the lazy initialization isn't ideal, but it's just about the only thing to lock on: this is a value type, so locking would box it and the lock would be on the box, not on the object itself. Since it's a value type, providing any reference type as a field to lock on would give a null reference, just like the array, so there's no help there. The only thing I can think to lock on is the type object itself for this type.

David Yaw
  • 27,383
  • 4
  • 60
  • 93