Can I have static data members in an abstract clas

2019-06-28 00:00发布

问题:

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?

回答1:

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.



回答2:

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;
}


回答3:

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#?



回答4:

You could try boost::assign::list_of, something like this:

vector<Resource*> AbstractClass::lResource = list_of( &res0 )( &res1 );


回答5:

I have few points here.

  • Your vectory probably having size of 0. This could lead to some crash. You will have to allocate it before using. You can give a static or global initialize.
  • Do you really want a vector? Vector is appropriate when you're using some dynamic memory allocation. The enums are static. You can give a simple count and allocate it as an array.
  • Do you really want a static member? Static member usually used while you're sharing it between the objects of the same class. Can you satisfy the requirement with an external objects which is local/global within the class? Also can you make static function out of the class?


回答6:

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.



回答7:

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();
}