I designed a series of related classes, and in order to be able to manage them I made them derive from a single abstract class.
These classes all need access to a series of shared resources, and I found myself creating a vector of pointers in each, all of them identical (they necessarily must be). It seems like making a static member in the base class would give all of the derived classes access to this vector, meaning I need only build it once (it's not going to change either after it's been built, just looked up).
My question is if this is ok, and if so, how can I then build it, without calling a 'fill the vector' method from one of the derived classes?
My thinking was to do something like
class Resource {};
enumR {RES0, RES1};
class AbstractClass
{
public:
virtual void OnInit() = 0;
void static fillVector(Resource* pResource, enumR Resourcename)
{lResource[Resourcename]=pResource;};
protected:
static vector<Resource*> lResource;
};
vector<Resource*> AbstractClass::lResource;
int main()
{
Resource res0, res1;
AbstractClass::fillVector(&res0, RES0);
AbstractClass::fillVector(&res1, RES1);
return 0;
};
Then when I instantiate an object of any class derived from AbstractClass, I'd have access to the lResource vector, which is what I want.
Would this work? Is it horrible? Is it ok?
The better solution would be to just make an object with the vectors in and then only instantiate it once and give the other classes a pointer or reference to it. Static data should be absolutely avoided unless necessary and this just isn't necessary.
You can add a static function to initialise your static vector:
class AbstractClass
{
private:
// Add this
static vector<Resource*> CreateResources();
protected:
static vector<Resource*> lResource;
};
vector<Resource*> AbstractClass::lResource = CreateResources();
vector<Resource*> AbstractClass::CreateResources()
{
vector<Resource*> resources;
resources[RES0] = new Resource();
resources[RES1] = new Resource();
return resources;
}
It would work, where work = compile & run.
However, all child classes will be accessing the same static vector, which means there won't be a different copy of the static vector for each child class.
For a better explanation of what I mean read the following So thread:
Are static fields inherited?
SOLUTION:
One solution is to have your parent class a template class as follows:
template<T>
class Parent<T> {
public:
static std::vector<T> sharedResource_;
}
class ChildA : Parent<ChildA> {
}
class ChildB : Parent<ChildB> {
}
In the above code, you will get a shared resource for all instances of ChildA and another one shared between instances of ChildB.
Is it right?
Well, I think it is not considered good. One of the related discussions to this is in comments to the following SO question and also under my answer to the question:
How to do "static overloaded const" in C#?
You could try boost::assign::list_of, something like this:
vector<Resource*> AbstractClass::lResource = list_of( &res0 )( &res1 );
Since you are creataing the vector of "resource pointer" and not reserving the spaces for object in advance ,your sysetem might crash in future. Why?
Vector create a block of memory when you insert element and uses the same block until it hits its capcity. Once it hits its capcality and you inset a new element, vector will allocate a new memory (twice as previous allocated memory) and copies all the existing elements into new memory. Since this is a vector of "pointers", it is going to invaliadate all the refernces.
What I usually do in these cases is add a middle templated layer so I have a structure like this:
Define your abstract interface:
class A
{
public:
virtual ~A(){}
virtual void f() = 0
virtual A* Clone() const = 0
}
Put the resources commonly used by the child in a template and define the boiler-plate function using CRTP if necessary keeping the necessary interface function still abstract
template<class Derived>
class A_T: public A
{
public:
virtual void f() = 0;
virtual A* Clone const
{
return new Derived(dyn_cast<Derived&>(*this))
}
void ManageRes();
private:
vector myResource;
}
Finally the different concrete classes complete the implementation and do something special with those resources if necessary
class Child: public A_T<Child>
{
public:
void foo();
}