在父类之前构造C ++成员类吗?

时间:2014-08-07 15:20:56

标签: c++ inheritance constructor

在场景中:

class A : public B {
private:
   C m_C; 
public:
   A();
}

A::A() : 
   m_C(5),
   B(m_C) 
{} 

法律?在B::B(m_C)之后会调用C::C(int)吗?如果是这样,我该怎么办才能避免呢?


这是如何实现的:

class MyValidator : public QRegExpValidator {
private:
    QRegExp myRegExp;
public:
    MyValidator(); 
    virtual void fixup(QString &f); 
}

MyValidator::MyValidator() 
    QRegExpValidator(QRegExp("foo")); 
{}

void MyValidator::fixup(QString &f){ 
    // A QRegExp("foo") is also needed here. 
}

我已经发现了

const QRegExp & QRegExpValidator::regExp() const; 

这减少了我自己对myRegExp的引用的需要,所以我的特定问题就解决了。

剩下的是,如果QRegExpValidator没有这样的功能来检索它的初始化程序,那么最好的模式是什么?手动将所有函数传递给成员类,而不是继承?

3 个答案:

答案 0 :(得分:6)

这就是C ++ 11标准所说的([class.base.init] / 10):

  

在非委托构造函数中,初始化按以下顺序进行:
   - 首先,仅对于派生程度最高的类(1.8)的构造函数,虚拟基类按照它们出现在基类的有向无环图的深度优先从左到右遍历的顺序进行初始化,其中“左” -to-right“是派生类 base-specifier-list 中基类出现的顺序。
   - 然后,直接基类按照它们出现在 base-specifier-list 中的声明顺序进行初始化(无论 mem-initializers 的顺序如何)。
   - 然后,非静态数据成员按照在类定义中声明的顺序进行初始化(再次与 mem-initializers 的顺序无关)。
   - 最后,执行构造函数体的复合语句   [注意:声明顺序的作用是确保以与初始化相反的顺序销毁基础和成员子对象。 - 结束记录]

因此,基类在非静态数据成员之前初始化。如果代码实际上以任何方式使用未初始化的数据,则可能会调用未定义的行为。


在评论中,我们讨论了一个可能的设计问题解决方案是使用has-a而不是is-a。你担心有一个会违反DRY(不要重复自己)。

使用has-a时,有两种方法可以避免WET(写入所有内容(至少两次))。一种是使用转换运算符,允许将对象传递给期望“派生”类型的函数。另一个是使用委托运营商。

class Object { /* ... */ };

class FakeDerivedObject {
    Foo foo;
    Object obj;
    //...

    // conversions
    operator Object & () { return obj; }
    operator const Object & () const { return obj; }
    operator Object * () { return &obj; }
    operator const Object * () const { return &obj; }

    // delegation
    Object * operator -> () { return &obj; }
    const Object * operator -> () const { return &obj; }
};

这样,您就不必“重新实现”底层对象的接口。

FakeDerivedObject d;

d->SomeMethodOfUnderlyingObject();
SomeFunctionThatExpectsUnderlyingObject(d);

答案 1 :(得分:1)

  

     

A::A() : m_C(5), B(m_C) {}

     

合法?

是。您可以按您选择的任何顺序列出初始值设定项。

  

在C :: C(int)

之后调用B :: B(m_C)

没有。编译器将忽略您列出初始值设定项的顺序,并按照它们在类声明中列出的顺序首先初始化base,然后按照它们在类声明中声明的顺序初始化。

请注意,大多数编译器都有一个警告级别,当初始化程序列出的顺序不是与初始化程序实际的调用方式相匹配时,它会发出警告。根据一些编码标准,需要按照调用它们的顺序列出初始化器。

答案 2 :(得分:0)

示例:

class Base {
public:
    Base(){}
};

class Derived: public Base {
public:
    Derived();

private:
    int _v1;
    int _v2;
};

构造顺序将是Base --> _v1 --> _v2