Reputation: 8298
I am quite new to real use of templates, so I have the following design question.
I am designing classes Bunch2d
and Bunch4d
that derive from a abstract base class Bunch
:
class Bunch {virtual void create()=0;};
class Bunch2d : public Bunch {void create();};
class Bunch4d : public Bunch {void create();};
The class Bunch
will contain a container, a deque
or a vector
(see this question: Choice of the most performant container (array)) of Particle
's:
typedef Blitz::TinyVector<double,DIMENSIONS> Particle;
You therefore see my question: Bunch
has to contain this container, because the "base" operations on my bunch are "dimension independant" (such a "size of the container", "clear container", etc.), so I think that the container belongs to the base class ("Bunch 'has a' container).
But this container has to know the dimensions (2 or 4) of the derived class.
So my idea would be to use a templated base class to give the typedef the correct dimension of the container:
enum Dimensions {TwoDimensions = 2, FourDimensions = 4, SixDimensions = 6};
template<Dimensions D> class Bunch
{
protected:
typedef Blitz::TinyVector<double,D> Particle;
std::deque<Particle> particles_store;
public:
virtual void create() = 0;
virtual ~Bunch();
};
class Bunch2d : public Bunch<TwoDimensions>
{
public:
~Bunch2d();
void create();
};
class Bunch4d : public Bunch<FourDimensions>
{
public:
~Bunch4d();
void create();
};
Can you give me your opinion on this design ? Would it be correct use of templates ? What about the validity of the OO concepts ? With a templated base class ?
Thanks for you help/answer/opinion.
Upvotes: 0
Views: 387
Reputation: 272497
For a start, Bunch<TwoDimensions>
and Bunch<FourDimensions>
are completely unrelated classes, so far as inheritance is concerned. Therefore, Bunch2d
and Bunch4d
have no common base class!
If this is going to be a problem for you, you'll have to do away with the templating, and have DIMENSIONS
parameterised at run-time.
Upvotes: 0
Reputation: 6869
You'd then loose the ability to have a pointer of Bunch class pointing to either Bunch2d or Bunch4d objects at runtime, and manipulate those objects polymorphically through that pointer. If it's important to you not to loose that, don't make the base class templated. Otherwise there is no point in having virtual functions and abstract base class here at all, so then I'd recommend going just with the template.
Upvotes: 2
Reputation: 299820
There is one single note: different template instances (ie template classes with different types in the parameters) are of different types, and therefore are NOT a single base class.
If you need polymorphism, you will need to add a layer in your design:
class Bunch
{
public:
virtual void create() = 0;
virtual ~Bunch();
};
template <Dimensions D>
class TBunch: public Bunch
{
private:
typedef Blitz::TinyVector<double,D> Particle;
std::deque<Particle> mParticles;
};
class Bunch2d : public TBunch<TwoDimensions>
{
public:
~Bunch2d();
void create();
};
On another note: protected
should be banned for attributes.
The issue is one of coupling, since protected
exposes the attributes / methods to an unknown number of classes, it's no different than public
in that it's impossible to reliably state how many methods will be affected by a change of implementation.
For methods, it's acceptable, because methods can be kept backward compatible (sometimes at the cost of some tricks / etc... but still).
For attributes, it's just unacceptable because an attribute is an implementation detail, not an interface, and a change cannot be made backward compatible.
Therefore I urge you not to EVER use protected
for an attribute. In this particular case, it would be a good idea to factor the accesses to mParticles
in the template class, without exposing the underlying implementation.
Small hint: if you cannot switch between deque
and vector
without breaking something beyond the class that holds them, then you have a design issue.
Upvotes: 2