views:

478

answers:

6

I have a hierarchy of classes. The base class uses some tuning parameters that are loadable from file (and reloadable during runtime). Each derived class may add some additional parameters. I am looking for a way to allocate a correctly sized parameters array in the base constructor, so that I don't have to deallocate and reallocate in the derived class. I was hoping for something like this, but it's not working (parameters always has 2 elements):

   class Base
   { static int nParms;
     virtual int getNParms() { return nParms;}
     float *parameters;
   public:
     Base() 
     { parameters= new float[this->getNParms()];}
       parameters[0] = globalReloadableX;
       parameters[1] = globalReloadableY;
     }
   };
   int Base::nParams =2;

   class Derived : public Base
   { static int nParms;
     virtual int getNParms() { return nParms;}
   public:
     Derived() : Base()
     { parameters[2] = globalReloadableZ;
     }
   }
   int Derived::nParams =3;

I've seen this question, but the solution there doesn't quite work for me. I also tried making parameters a regular array in each class:

  class Base
  {  float parameters[2]
    ...
  class Derived : public Base
  {  float parameters[3]
    ...

but that makes Derived have 2 separate arrays.

Any ideas?

+5  A: 

Why not pass the required array size as a parameter in the constructor of the base class?

(The reason the virtual function doesn't call the derived class is because that is how C++ virtual functions work; conceptually, until the derived class constructor completes, the object's type is still the base class.)

Daniel Earwicker
Why not? Too obvious! Sometimes I just get too wrapped up in the details... Thanks for the explanation of why.
AShelly
No problem, happens to everyone!
Daniel Earwicker
+2  A: 

What about making the size a parameter?

class Base
{ static int nParms;
  virtual int getNParms() { return nParms;}
  float *parameters;
public:
  Base(int n = nParams) 
  { parameters= new float[n];
    parameters[0] = globalRelodableX;
    parameters[1] = globalRelodableY;
  }
};
int Base::nParams =2;

class Derived : public Base
{ static int nParms;
  virtual int getNParms() { return nParms;}
public:
  Derived() : Base(nParams)
  { parameters[2] = globalRelodableZ;
  }
}
int Derived::nParams =3;
Ryan Graham
+2  A: 

Why use an array? The std::vector will allow you to use as many params as needed in the derived class, with the base not knowing (or caring) how many it needs.

Harper Shelby
That's a good point. I'm actually using vectors in other places. But I still do most of my work in C, so I tend to go for what I know when I'm adding features...
AShelly
A: 

I would consider using a std::map. It can grow with base and derived both not caring about the number of parameters the other uses. Key/value pairs are likely easier to manage that numeric indexes, though that is clearly application dependent.

Steve Fallows
A: 

I like both Earwicker and Steve's answers, depending on what's required. If there's many of these objects being created and destroyed frequently, then you want the minimum amount of memory allocation possible, and thus Earwicker's is superior. However, if this is something that's usually "created and rarely re-made" then I'd go with Steve's answer, as maps are just generally a lot easier to work with, and grow dynamically as needed, but is probably too much overhead if this object is something being made and destroyed a lot.

Kevin
+1  A: 

You could make it a parameter to the constructor, as suggested by others, but you could also make Base a template class, with the size as the parameter. This has many advantages, such as removing the need for the array to be allocated on the heap:

template <size_t nParams>
class Base
{
    float parameters[nParams];
public:
    Base()
    { // could use a static_assert(nParams > 1) here...
      parameters[0] = globalRelodableX;
      parameters[1] = globalRelodableY;
    }
};

class Derived : public Base<3>  // or whatever
{
public:
    Derived()
    { parameters[2] = globalRelodableZ; }
};
Alastair