order of object initialization and the use of constructor initializer

Discussion in 'C++' started by Jess, Apr 25, 2007.

  1. Jess

    Jess Guest

    Hello,

    When I define default constructors, I tend to use constructor
    initializers for member data. However, I was told the order in which
    members are initialized is determined by the order of declaration in
    the class, instead of the order they appear in the constructor
    initializer. This would introduce interdependencies. Therefore, it
    is safer to avoid such interdependence by assigning values to these
    members inside the constructor body rather than initializing them in
    the intializer. Could someone explain to me why this is the case?
    Moreover, what kind of error could possibly happen due to the
    "interdependency"?

    Many thanks!
     
    Jess, Apr 25, 2007
    #1
    1. Advertisements

  2. The order of construction is reversed for destruction. If you have
    multiple constructors for the class, the order of construction is always
    the same, as you said, no matter how the initializers are ordered.

    Some compilers will issue a warning if the initializer list is not
    consistent with the order of member declarations.

    As for issues of interdependencies, I don't think I have ever come
    across a situation where this was a problem.
     
    Gianni Mariani, Apr 25, 2007
    #2
    1. Advertisements

  3. They *still* get initialised in the order of declaration, but using the
    default constructor (provided it exists).

    And you have no choice, if the members don't have a default constructor,
    or their assignment operator is not defined, or it has a different
    effect from the constructor.
     
    Richard Herring, Apr 26, 2007
    #3
  4. Jess

    Jess Guest

    If I have a class like:

    class A{
    int x;
    int y;
    string z;
    B b;
    A () : x(0), y(0) { x = 1;}
    }

    Then if I have a code that is "A a;" then I think the following
    happens:
    -- A() is called
    -- x -> 0, y -> 0
    -- z becomes null string
    -- b is initialized by calling B's constructor
    -- body of A() is entered, and x -> 1.

    Is this right?
    If A() is changed to:
    A () : y(0), x(0) { x = 1;}

    Then will the compiler complain? Thanks

    Jess
     
    Jess, Apr 27, 2007
    #4
  5. ... because that's how the default string c-tor behaves.
    ".. by calling B's _default_ constructor"
    1 -> x
    Pretty much.
    I don't know, you need to ask the compiler.

    V
     
    Victor Bazarov, Apr 27, 2007
    #5
  6. There are cases where one attribute needs to be passed into a
    superclass constructor or into another attribute's constructor and
    these can cause problems.

    Where you're passing between attributes then you should order them
    correctly in the class definition. Where you have to pass into the
    superclass I've done something like this (heavily abridged):

    // Super's constructor needs an int pointer to use
    class Super { Super( int * ); };

    // We store the int here
    class Holder { int m_attribute; };

    // We can now get the int constructed before Super needs it
    class Sub : Holder, Super { Sub(); };

    // Pass the int to Super
    Sub::Sub()
    : Holder(), Super( &m_attribute ) {}

    As I understand it the order of the super-classes in the definition of
    Sub is important, not the order of the calls in the definition of
    Sub's constructor. This is analogous to the case with the attribute
    order in the class definition.


    K
     
    =?iso-8859-1?q?Kirit_S=E6lensminde?=, Apr 27, 2007
    #6
  7. Jess

    James Kanze Guest

    It might issue a warning, but the code is legal, and does
    exactly what the previous code does.

    The only time this is a problem is if you write something like:

    A::A() : y( 0 ), x( y ) { x = 1; }

    In this case, you actually have undefined behavior, because you
    try to initialize x with y before having initialized y.
     
    James Kanze, Apr 27, 2007
    #7
  8. Jess

    Marcus Kwok Guest

    Here is a contrived example, but it demonstrates the point:


    #include <iostream>

    struct Foo {
    const int c;
    int i;

    Foo(int x) : c(x), i(2 * c) { }
    };


    struct Bar {
    int i;
    const int c;

    Bar(int x) : c(x), i(2 * c) { }
    };


    int main()
    {
    using std::cout;

    Foo f(3);
    cout << f.c << ", " << f.i << '\n';

    Bar b(3);
    cout << b.c << ", " << b.i << '\n';
    }


    Output:
    3, 6
    3, 8529120


    In Foo, first the const int c is initialized with the value of 3, then
    the int i is initialized with the value of (2*3).

    In Bar, since i was declared before c, then i's initialization occurs
    first. At this point the value of c is garbage (and thus reading it is
    really undefined behavior), so i is initialized with 2*garbage before c
    is initialized.
     
    Marcus Kwok, Apr 27, 2007
    #8
  9. Jess

    Jess Guest

    Many thanks for your explanations!

    Jess
     
    Jess, Apr 28, 2007
    #9
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.