【问题标题】:initialize variable in class before calling parent's constructor in c++在 C++ 中调用父级的构造函数之前初始化类中的变量
【发布时间】:2015-06-03 19:20:23
【问题描述】:

如果我有这些课程:

class A
{
    int x,y;
    public: 
        A(const int &x,const int &y):x(x),y(y){} 
};
class B:public A
{
    int z;
    public :
       B(const int &x,const int &y,const int &thez):z(thez),A(x+z,y+z)
};

我想在调用 A 的构造函数之前在 B 类中初始化 z,但正如我从调试 A 的构造函数中发现的那样,无论它放在哪里,总是首先被调用。

这样做的真正目的是计算由三个立方体(头、身体和腿)组成的 Player 类(在桌上足球中找到的 Player)的转动惯量,这三个立方体在 Player 构造函数和 Player 中初始化惯性在 Body 的构造函数中初始化(Body 是 Player 的父级)。

我的问题是播放器的惯性取决于立方体的惯性,我计算它们,我想对它们求和并在其上调用父对象,但我无法在不初始化立方体的情况下对它们求和(它们之前什么都不是初始化)。

那么怎么做呢?

PS

我知道我可以把关系和总和,好吧,但是它很长,如果我这样做,构造函数会很容易变得丑陋,我认为这只是最后的手段。

【问题讨论】:

  • 基类依赖于派生类几乎总是糟糕的设计。
  • @Pubby,除非你使用 CRTP。
  • @Pubby 例外是抽象类?
  • @RSahu Comparable in java 是 CRTP 的一个例子
  • 你建房子不先打地基吗?从屋顶开始是愚蠢的

标签: c++ constructor


【解决方案1】:

除了这样做是否是一个好主意之外:如果您想在基类成员之前初始化派生类成员,您可以使用base-from-member-idiom:只需将相应的成员放入另一个基类中即可确保它首先被初始化。

class A
{
    int x,y;
    public: 
        A(const int &x,const int &y):x(x),y(y) {} 
};

class Z
{
    int z;
    Z(const int &thez): z(thez) {}
};

class B: public Z, public A
     //  ^^^^^^^^ 
     // Z must come before A
{
    public :
       B(const int &x,const int &y,const int &thez): Z(thez), A(x+z,y+z) {}
};

基类Z——尤其是它的成员z——将在A的成员之前初始化。

免责声明:一般来说,要小心多重继承及其diamond of death

【讨论】:

  • 这样可以解决问题,但是我不想多继承,我知道以后会后悔的
  • @niceman:不确定:将其限制在最基本的要素上,并可能派生出privately。看这里Stroustroup says about MI。您没有太多其他机会,否则派生类成员将在基类成员之后被初始化。
【解决方案2】:

其他答案为如何解决您的问题提供了一些建议 - 我的建议是稍微不同地考虑问题。

这似乎是using composition instead of inheritance 可能是个好主意的情况。

在您的情况下,您说“Body”是“Player”的父级,这让我认为这可能是一个可以重新考虑的设计选择。我会考虑这样考虑:

  • 创建一个名为 ThingWithInteria 之类的抽象父类,它定义了用于获取对象惯性的 API。
  • 创建一个 BodyPart 类,其中包含与每个身体部位相关的值(在您的情况下,这可能是质量和距中心的距离?),并使用对这些值的一些简单计算来实现 ThingWithInteria API。李>
  • 将 Player 类更改为也继承自 ThingWithInertia。它将包含许多 BodyParts(在您的示例中为“head”、“body”和“legs”)。
  • 在 Player 的构造函数中,在进入函数构造函数的主体之前初始化每个 BodyPart。这样就很容易将整体惯性计算为各个零件惯性的函数。

这个类层次结构反映了 Player 和 BodyPart 之间的关系不是“是”关系,而是“有”(或“有一些”)关系。虽然它们可能共享一个用于访问惯性的通用 API,但它们实现它的方式是不同的,因为它们代表着根本不同的事物。

虽然这不能解决您在调用父构造函数之前询问的有关如何初始化成员变量的特定编程问题,但我认为值得一提的是,以防其他人由于做出一些类似的设计选择而发现这个问题带领他们走到了这一步。

【讨论】:

    【解决方案3】:

    如果您在将“z”传递给父类之前没有修改它,那么您应该像这样将“z”传递给父类:

    class B:public A
    {
        int z;
        public :
           B(const int &x,const int &y,const int &thez):A(x+thez,y+thez), z(thez)
    };
    

    这样,父类将“z”值添加到其“x”和“y”中,您还可以存储“z”的值以供以后使用。

    另一种方法是使用派生类构造函数来修改继承的值,如下所示:

    class B:public A
    {
        int z;
        public :
           B(const int &x,const int &y,const int &thez):A(x,y), z(thez)
           {
               //modifications to z
               x +=z;
               y +=z;
           }
    };
    

    这仅在您没有在父构造函数中进行其他修改时才有效。

    【讨论】:

    • 我正在修改z,然后再将其传递给父级:)
    • 基类成员是私有的,所以派生类构造函数不能修改它们
    • @mathematician1975 确切地说:3
    猜你喜欢
    • 2013-02-28
    • 1970-01-01
    • 2021-12-21
    • 2014-05-19
    • 2014-10-23
    • 1970-01-01
    • 2012-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多