C ++中静态成员的多态性和继承

时间:2009-12-21 18:27:54

标签: c++ inheritance static

我需要为每个班级保留一份儿童名单(矢量),我有一个棘手的问题:

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

添加A(0),B(1,1)和C(2,2,2)并调用a.ShowChildren给出:1,1; 2,2,2; 2,2,2-

每次创建C类实例时,都会更新A :: children而不是B :: children和A :: children。 Sooo ... C类被添加到A类的子代中两次,但没有添加到B类。当我将AddChildren类(字面上副本)复制到B类时,它会有所帮助。 class有自己的AddChildren / ShowChildren。此外,我已经设法使用指针完成此任务,但我想知道是否有更好的方法。我认为问题出在“使用正确的向量”的某个地方,但我不知道如何强制编译器使用正确的向量。

对于我在这里做错的任何建议,我将不胜感激。

首先,谢谢大家的意见和帮助。使用你的建议(关于我的设计和虚拟GetList())我设法简化了我的程序:

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

现在它使用构造函数而不调用上层类,它只调用上层类的正确构造函数。它不是最好的,但我认为它更好。

再一次,谢谢大家的帮助。

5 个答案:

答案 0 :(得分:4)

编辑:具有讽刺意味的是,这不适用于您发布的案例。我将其取消删除,因为它可能在其他情况下有用。

当您调用AddToChildren()时,您将获得A的实现,当然,它会添加到A的静态成员中。

这是因为C ++没有“虚拟数据”的概念。一种方法是添加一个名为GetList()的虚函数。我看起来像这样(未经测试的代码):

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

和B:

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

然后将AddToChildren更改为:

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

答案 1 :(得分:3)

函数的代码仅适用于它所定义的类,即使它是虚拟的。因此,以下函数始终应用于类A,因此附加到A::children

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

如果你想要每个类的不同向量,你别无选择,只能重复从一个类到另一个类的代码(静态变量,它的初始化,以及一个add-to-children函数或一个get-children-list函数) )。不过,我建议不要在构造函数中调用虚函数。

您可能感兴趣的另一种方法是为此类存储设置一个独特的类模板:

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

答案 2 :(得分:1)

你的设计意图不够明确,这就是为什么其他一些答案的作者在答复中感到困惑的原因。

在您的代码中,您似乎从某些构造函数调用AddToChildren,而不是从其他构造函数调用children。例如,您在A中有一个AddToChildren列表,但您从未在A::A构造函数中调用C。此外,班级children没有自己的children列表。为什么?是否应该与B分享AddToChildren列表?

我可以猜测,你没有从所有构造函数中调用AddToChildren这意味着某些构造函数旨在构建给定类型的完整“对象”(这些构造函数会调用AddToChildren),而一些其他构造函数旨在被后代类用作“中间”构造函数(这些构造函数不调用C::C)。

这种设计可能被认为是非常值得怀疑和容易出错的。请注意,例如,AddToChildren调用this,据称正在向B::children添加B::B(是意图?),还调用this构造函数,还会将B::children添加到this。因此,相同的GetList值会两次添加到列表中。这似乎没有任何意义。

你需要弄清楚你要做的是什么,然后修复你的设计。完成后,您可以使用Neil提出的技术“虚拟化”列表(引入虚拟B方法)。尼尔后来写错了,认为它不起作用。事实上,它将完美地工作(再次,假设我正确理解您的设计)。


(考虑到OP的澄清评论)

因此,您希望将A::children个对象添加到C列表和A::children个对象,以添加到B::childrenclass 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; } ... }; 列表中。这可以通过

来实现
AddToChildren

请注意,尽管其他海报所说的内容,虚拟调用在这里工作,但它们的工作方式正如我们需要它们来实现所请求的功能一样。但请注意,在这种情况下,将方法GetList设为虚拟是没有意义的,仅AddToChildren的虚拟性就足够了。

此外,如果push_back只做一个AddToChildren,整个事情就会变得很少。没有多少意义为单独的这种“瘦”{{1}}构建这样的基础设施。只需在每个构造函数中明确地执行您想要执行的操作。

答案 3 :(得分:0)

您尚未向我们展示A::AddToChildrenB::AddToChildren的实施情况,但这就是问题所在。无论是一种还是另一种方式,B::AddToChildren的实现都会增加错误的向量。也许你没有专门研究B的方法?如果没有看到那部分代码,就无法真正说出来。

在评论后进行修改:如果您坚持在此处使用继承,则可以执行以下操作:

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

在这种情况下,我坦率地认为这更令人困惑,但并非如此。

这与Neil Butterworth上面所说的非常类似,但对于任何调用C :: AddToChildren(A * obj)的人来说也有一点安全。

答案 4 :(得分:-1)

@Neli:这种方法无法解决这个具体问题,因为在C ++中,析构函数/构造函数对虚函数的调用实际上并不是虚拟的。因此,构造函数GetList()仍将返回A的子项。

P.S。这应该是对回复的评论,但我找不到合适的选项..

P.P.S。特别为AndreyT带来爱情

请仔细阅读以下内容。它来自C ++国际标准,12.7。

当直接或间接地从构造函数(包括来自数据成员的mem-initializer)或析构函数调用虚函数时,调用所适用的对象是正在构造或销毁的对象,该函数调用是在构造函数或析构函数自己的类或其基础中定义的,但不是在从构造函数或析构函数的类派生的类中重写它的函数,或者在最派生的其他基类之一中重写它对象(1.8)。如果虚函数调用使用显式类成员访问(5.2.5)并且object-expression引用正在构造或销毁的对象但其类型既不是构造函数或析构函数自己的类或其基础之一,那么结果是电话未定义。