开发者

Polymorphism and inheritance of static members in C++

I need to keep a list(vector) of children for every class, and I've got a tricky problem:

class A
{
protected:
    int a;
    static vector<A*> children; 
public:
    A(int a): a(a) {;};
    virtual void  AddToChildren(A* obj);
    virtual void  ShowChildren();
    virtual void Show();
};

class B: public A
{
protected:
    int b;
    static vector<A*> children;
public:
    B(int a, int b): b(b), A(a) { A::AddToChildren(this);};
    virtual void Show();
};

class C: public B
{
protected:
    int c;

public:
    C(int a, int b, int c): c(c), B(a,b) { B::AddToChildren(this);};
    virtual void Show();
};

vector<A*> A::children=vector<A*>();
vector<A*> B::children=vector<A*>();


void A::AddToChildren(A *obj)
{
    children.push_back(obj);
}

void A::ShowChildren()
{
    for(vector<A*>::iterator i=children.begin(); i!=children.end();i++)
        (*i)->Show();
}

Adding A(0), B(1,1) and C(2,2,2) and calling a.ShowChildren gives: 1,1 ; 2,2,2 ; 2,2,2

Every time I make an instance of class C the A::children is updated instead of B::children and A::children. Sooo... the class C is added twice to the children of A class, but not added to class B. It helps when I copy the AddChildren class (literally copy) to class B, so that every class has its own AddChildren/ShowChildren. Also I've managed to accomplish this task using pointers, but I'm wondering is there a better way. I think that the problem is somewhere in the "using the right vector", but I don't know how to force开发者_如何学运维 the compiler to use the right one.

I would be grateful for any suggestions on whatever I'm doing wrong here.

First of all, thank you all for your comments and help. Using your advice (about my design and virtual GetList()) I managed to simplify my program:

class A
{
protected:
    int a;
    virtual vector<A*>* GetList();
public:
    A(int a): a(a) {;};
    A(int a, A* inherited):a(a) { AddToChildren(inherited);};

    static vector<A*> children; 

    virtual void AddToChildren(A* obj);
    virtual void ShowChildren();

    virtual void Show();
};

class B: public A
{
protected:
    int b;
    virtual vector<A*>* GetList();
public:
     static vector<A*> children;
     B(int a, int b): b(b), A(a,this){;};
     B(int a, int b, A* inherited) : b(b), A(a,this){AddToChildren(inherited);};

    virtual void Show();

};

class C: public B
{
protected:
    int c;
public:
    C(int a, int b, int c): c(c), B(a,b,this) { };
    virtual void Show();
    virtual vector<A*>* GetList();
};


vector<A*> A::children=vector<A*>();
vector<A*> B::children=vector<A*>();


void A::AddToChildren(A *obj)
{
    GetList()->push_back(obj);
}

void A::ShowChildren()
{
    for(vector<A*>::iterator i=GetList()->begin(); i!=GetList()->end();i++)
        (*i)->Show();
}


vector<A*> * A::GetList()
{

    return & children;
}

vector<A*> * B::GetList()
{

    return & children;
}

vector<A*> * C::GetList()
{

    return & children;
}

Now its using constructors without calling the upper class, it just calls the proper constructor of the upper class. Its not the best, but i think it's better.

Once more, thank you all for help.


Edit: as ironic points out, this does not apply in the case you posted. I'm leaving it undeleted as it may be useful in other situations.

When you call AddToChildren(), you get A's implementation, which (of course) adds to A's static member.

This is because C++ has no concept of "virtual data". One way round it would be to add a virtual function called GetList(). I A it looks like this (untested code):

virtual vector <a*> * GetList() {
   return & A::children;
}

and in B:

virtual vector <a*> * GetList() {
   return & B::children;
}

Then change AddToChildren to:

void A::AddToChildren(A *obj)
{
    GetList()->push_back(obj);
}


The code of a function only applies to the class it is defined in, even if it is virtual. So, the following function always applied to class A and therefore appends to A::children :

void A::AddToChildren(A *obj)
{
  children.push_back(obj);
}

If you want distinct vectors for every class, you have no choice but to repeat code from one class to another (the static variable, its initialization, and either an add-to-children function or a get-children-list function). I would advise against calling virtual functions in constructors, though.

Another approach you might be interested in would be to have a distinct class template for such storage:

template<typename T> struct children 
{
  static std::vector<T*> list;
  static void add(T* t) { list.push_back(t); }
};

B::B() : A() {
  children<A>::add(this);
}

C::C() : B() {
  children<B>::add(this);
}


Your design intent is not sufficiently clear, which is why the authors of some other answers got confused in their replies.

In your code you seem to make calls to AddToChildren from some constructors but not from the others. For example, you have a children list in A but you never call the AddToChildren from A::A constructor. Also, class C has no its own children list. Why? Is it supposed to share the children list with B?

I can guess that the fact that you are not calling AddToChildren from all constructors means that some constructors are intended to build complete "objects" of given type (these constructors do call AddToChildren), while some other constructors are intended to be used as "intermediate" constructors by descendant classes (these constructors don't call AddToChildren).

Such design might be considered quite questionable and error prone. Note, for example, that C::C calls AddToChildren, which supposedly is adding this to B::children (was it the intent?), and also invokes B::B constructor, which will also add this to B::children. So, the same this value is added to the list twice. This does not seem to make any sense.

You need to figure out what is it you are trying to do and then fix your design. Once you are done with it, you can "virtualize" the list using the technique proposed by Neil (introducing a virtual GetList method). Neil later wrote incorrectly that it will not work. In fact, it will work perfectly fine (again, assuming that I understand your intended design correctly).


(Taking into account the OP's clarifying comments)

So, you want B objects to be added to A::children list and C objects to be added to both A::children and B::children lists. This can be achieved by

class A {
  ...
  int a;
  static vector<A*> children;
  ...
  A(int a) : a(a) {}

  virtual vector<A*> *GetList() = 0;

  void AddToChildren(A* obj) { // note: non-virtual
    GetList()->push_back(obj);
  }
  ...
};

class B : public A {
  ...
  int b;
  static vector<A*> children;
  ...
  B(int a, int b) : b(b), A(a) { 
    AddToChildren(this);
  }

  virtual vector<A*> *GetList() {
    return &A::children;
  }
  ...
};

class C : public B {
  ...
  int c;
  ...
  C(int a, int b, int c) : c(c), B(a,b) { 
    AddToChildren(this);
  };    

  virtual vector<A*> *GetList() {
    return &B::children;
  }
  ...
};

Note that despite what was said by other posters, virtual calls do work here and they work exactly as we need them to work to achieve the requested functionality. Note though, that in this case there's no point to make method AddToChildren virtual, the virtuality of GetList alone is sufficient.

Also, the whole thing makes little if AddToChildren just does a push_back. There's no much sense the build such infrastructure for such a "thin" AddToChildren alone. Just do what you want to do explicitly in each constructor.


You haven't show us the implementation of A::AddToChildren or B::AddToChildren, but that's where the problem will be. One or another way, your implementation of B::AddToChildren is adding to the wrong vector. Maybe you didn't specialize the method for B? Can't really tell without seeing that part of the code.

Edit after comments: If you insist on using inheritance here, you could do something like:

class A
{
    ...
    virtual vector<A*> * ChildrenPtr();
};

...

A::ChildrenPtr() {return &A::children;}
B::ChildrenPtr() {return &B::children;}
C::ChildrenPtr() {return NULL;}

void A::AddToChildren(A *obj)
{
    vector<A*>  * pChildren = ChildrenPtr();
    if (pChildren)
        pChildren->push_back(obj);
}

In this case, I frankly think that's more confusing, though, not less.

This is very similar to what Neil Butterworth said above, but also has a bit of safety about anyone ever invoking C::AddToChildren(A *obj).


@Neli: Such approach would not solve this concrete problem because in C++ calls to virtual functions from destructors/constructors are actually not virtual. So from the constructor GetList() will still return children of A.

P.s. this should be in comment to the reply, but I could not find appropriate option..

P.P.S. Specially for AndreyT with love

Please read the following carefully. It is from C++ international standard, 12.7.

When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor’s own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructor’s class, or overriding it in one of the other base classes of the most derived object (1.8). If the virtual function call uses an explicit class member access (5.2.5) and the object-expression refers to the object under construction or destruction but its type is neither the constructor or destructor’s own class or one of its bases, the result of the call is undefined.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜