【问题标题】:Base and Derived class constructor polymorphism woes基类和派生类构造函数多态性问题
【发布时间】:2013-02-04 22:14:34
【问题描述】:

抱歉标题含糊。

我正在编写一组类,但我很难想出一种直观的方法来分解基构造函数中所做的事情和派生构造函数中所做的事情。

我的问题如下:

所有课程都要求按顺序完成步骤 A、C 和 E。因此,将它们放在基础构造函数中是有意义的。

步骤 B 和 D 对于每个派生类都是唯一的。

不幸的是,B必须在之后 A和之前 C完成,同样地,D必须在之后 C和之前 E. 这些是 OpenGL 函数调用,我受限于它们的顺序。

我最初尝试过类似这样的东西,希望尽可能地封装在基类中:

public class MyBase
{
    MyBase()
    {
        A():
        B();
        C();
        D();
        E();  
    }
    void A() {...}
    void C() {...}
    void E() {...}
    virtual void B() = 0;
    virtual void D() = 0;
}

public class MyDerived : public MyBase
{
    MyDerived() : MyBase() {}
    void B() {...}
    void D() {...}
}

但这在 C++ 中是不可能的...

除了以下之外还有更直观的方法吗:

public class MyBase
{
    MyBase() {}
    void A() {...}
    void B() {...}
    void C() {...}
}

public class MyDerived : public MyBase
{
    MyDerived()
    {
        A():
        B();
        C();
        D();
        E();  
    }
    void B() {...}
    void D() {...}
}

我希望尽可能避免代码重复。

有什么想法吗?

【问题讨论】:

  • 这些方法一定要在构造函数中调用吗?我经常发现只有一个称为 post-construction 的“初始化”方法更容易。只要每个继承的类和成员都有一个默认构造函数,那么这样做就不会造成任何障碍。如果每个类都派生自一个公共基类,您甚至可以将它们全部放在一个向量上并在循环中调用初始化方法。
  • 我认为将CE 移动到派生类型可能会更好。我很难想象这种情况。

标签: c++ inheritance polymorphism


【解决方案1】:

如果您确实想在构造函数中调用它,并且在之后调用它又不行,您可以通过执行以下操作来减少混乱。

public class MyBase
{
    protected:
    void templateMethod()
    {
        A():
        B();
        C();
        D();
        E();  
    }
    private:
    void A() {...}
    void C() {...}
    void E() {...}
    virtual void B() = 0;
    virtual void D() = 0;
};

public class MyDerived : public MyBase
{
    MyDerived() : MyBase() {templateMethod();}
    void B() {...}
    void D() {...}
};

但这并不是一个真正的模板方法模式,因为现在所有派生类都必须调用它,而不是为基类调用一次。

【讨论】:

  • 只有一个问题:在 C++ 中,是否保证在调用 MyDerived 的 ctor 时初始化 vftable?我相信这不是一种已定义的行为,因此在 ctor 中调用虚拟方法可能很危险。
  • @AdrianShum:当MyDerived 的构造函数开始时,vftable 已经更新到正确的位置。
  • @AdrianShum 我相信这是很好地定义了派生类,任何派生派生类都会导致问题。这不适用于非叶类不是抽象的层次结构
【解决方案2】:

鉴于在构造函数中调用虚函数可能很危险,使用宏是您可以接受的解决方案吗?

#define MY_BASE_INIT A();B();C();D();E();

public class MyBase
{
    MyBase() {}
    void A() {...}
    void C() {...}
    void E() {...}
}

public class MyDerived : public MyBase
{
    MyDerived()
    {
        MY_BASE_INIT
    }
    void B() {...}
    void D() {...}
}

【讨论】:

  • 将调用放入宏有什么帮助?而MyBase是如何获取它的三个方法调用的呢?
  • 事实上,它只是您的第二个解决方案。我只是将一系列方法调用放在一个宏中。请注意,宏用在派生类的ctor中,而不是基类中
  • 确切地说,我的问题是,如果基本构造函数为空,从宏调用与直接调用有何不同?从编译器的角度来看,应该是一样的
  • 是一样的 :) 与原来的第二种方法相比,不同之处只是减少了重复的样板代码(我认为这就是你正在寻找的,因为你说“我希望尽可能避免代码重复。”)
猜你喜欢
  • 1970-01-01
  • 2016-12-20
  • 2016-07-19
  • 1970-01-01
  • 2020-11-28
  • 2018-07-21
  • 2015-08-18
  • 2018-07-16
  • 2015-11-03
相关资源
最近更新 更多