Are C ++ class classes built before parent classes?

In the script:

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

      

there is

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

      

legal? Call B::B(m_C)

after C::C(int)

? If so, what should I do to avoid this?


How it happened in practice:

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

      

I have already discovered

const QRegExp & QRegExpValidator::regExp() const; 

      

This saves me from having to keep my own link to myRegExp

, so my specific problem is solved.

what remains would be a better option if it QRegExpValidator

doesn't have such a function to fetch its initializer .. manually including all functions in a member class instead of inheriting?

+3


source to share


3 answers


This is what the C ++ 11 standard ([class.base.init] / 10) has to say:

In a non-delegated constructor, initialization is performed in the following order:
- First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order in which they appear in the first backward direction from left to right directed to the acyclic base class graph, where "left -to-right" is the order in which the base classes appear in the base-specifier of the derived class.
- The direct base classes are then initialized in the order of declaration as they appear in the base-specifier-list (regardless of the order of the mem initializers).
- Then non-static data members are initialized in the order in which they were declared in the class definition (again, regardless of the order of mem initializers).
- Finally, the compound statement of the constructor body is executed.
[Note. The declaration order is authorized to ensure that the base and member subobjects are destroyed in the reverse order of initialization. - end note]

So, the base class is initialized before non-static data members. Your code can cause undefined behavior if it actually uses uninitialized data in any way.


In the comments, we discussed one possible solution to your design problem - use has-a rather than is-a. You were worried that has - a will break DRY (don't repeat yourself).



There are two ways to avoid WET (write everything (at least) twice) when using has-a. One of these uses conversion operators that let you pass your object to a function that expects your "derived" type. Another uses the delegation operator.

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

      

This way, you don't need to "reimplement" the interface of the base object.

FakeDerivedObject d;

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

      

+5


source


there is

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

legal?

Yes. You can list the initializers in any order you choose.

B :: B (m_C) will be called after C :: C (int)



Not. The compiler will ignore the order in which you specified the initializers and initialize the bases first in the order in which they appear in the class declaration, and then the members in the order in which they are declared in the class declaration.

Note that most compilers have a warning level that will warn you when initializers are listed in a different order from how initializers are triggered. Some coding standards require you to list the initializers in the order in which they will be called.

+1


source


Example:

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

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

private:
    int _v1;
    int _v2;
};

      

The build order will always be Base --> _v1 --> _v2

0


source







All Articles