Initializing member variables in the constructor

by .

As you might know, it is good practice to leave no variables uninitialized. If you wrote a struct or class, all its members should have a defined initial state, which is usually set in the struct’s or class’s constructor.

Basics

Let’s say we have a struct like this:

struct MyStruct
{
  LONG _i;
  Real _x;
  Vector _v;
};

To give all members a defined initial state, add a constructor:

struct MyStruct
{
  LONG _i;
  Real _x;
  Vector _v;

  MyStruct()
  {
    _i = 0;
    _x = RCO 0.0;
    _v = Vector(RCO 0.0);
  }
};

Now we have a defined initial state for each member variable. Crashes and surprises caused by uninitialized variables are now impossible.

But let’s take a close look at what happens… when an instance of the struct is constructed, the memory for each of the member variables is allocated. Then, in the constructor, the variables are filled with values that have also been constructed. So, all in all, we have double as much constructions as necessary.

A better idea is this:

struct MyStruct
{
  LONG _i;
  Real _x;
  Vector _v;

  MyStruct() : _i(0), _x(RCO 0.0), _v(RCO 0.0)
  {  }
};

This way, the member variables are constructed with their initial value, instead of being constructed and then filled with their initial values. The difference may seem small, but in a time-critical it pays off.

Note: It is important to initialize the member variables in the very same order in which they were declared!

More constructors

Of course, overloaded constructors offering the possibility to pass initial values or to copy them from another instance of the same struct are also possible:

struct MyStruct
{
  LONG _i;
  Real _x;
  Vector _v;

  // Default constructor
  MyStruct() : _i(0), _x(RCO 0.0), _v(RCO 0.0)
  {  }

  // Copy constructor
  MyStruct(const MyStruct& src) : _i(src._i), _x(src._x), _v(src._v)
  {  }

  // Constructor for passing initial values
  MyStruct(LONG i, Real x, const Vector& v) : _i(i), _x(x), _v(v)
  {  }
};

If members have to be allocated

If you have any member that needs to be allocated, e.g. an array, then you must either do that in the constructor body (done e.g. in the copy constructor), or initialize the pointer with NULL (done in the default constructor) and allocate it later at some point. Also, take care to free the memory, of course.

struct MyStruct
{
  LONG _i;
  Real _x;
  Vector _v;
  Real* _arr;

  // Default constructor
  MyStruct() : _i(0), _x(RCO 0.0), _v(RCO 0.0), _arr(NULL)
  {  }

  // Copy constructor (Array copied manually from the source!)
  MyStruct(const MyStruct& src) : _i(src._i), _x(src._x), _v(src._v)
  {
    if (_i > 0 && src._arr)
    {
      _arr = GeAllocTypeNC(Real, _i);
      if (_arr)
        CopyMemType(Real, src._arr, _arr, _i);
    }
  }

  // Constructor for passing initial values
  MyStruct(LONG i, Real x, const Vector& v) : _i(i), _x(x), _v(v)
  {
    if (_i > 0)
    {
      _arr = GeAllocType(Real, _i);
    }
  }

  // Destructor, to free the array
  ~MyStruct()
  {
    if (_arr)
      GeFree(_arr);
  }
};

One thing you might notice is that GeAllocTypeNC() is used in the copy constructor. Of course, GeAllocType() would also do the job, but GeAllocTypeNC() does not initialize the allocated memory with 0, so we save a bit of time. The array is filled with values right after that, anyway, using CopyMemType().

Advertisements