How is member initialisation ordered?

Discussion in 'C++' started by Jason Heyes, Dec 3, 2003.

  1. Jason Heyes

    Jason Heyes Guest

    Here is a program that doesn't work as intended:

    #include <iostream>
    using namespace std;

    class MyClass
    {
    int bar;
    int foo;
    public:
    MyClass(int foo_, int bar_) : foo(foo_), bar(bar_ + foo) { }
    void print() const { cout << foo << " " << bar << endl; }
    };

    int main()
    {
    MyClass obj(3,6);
    obj.print(); // intended to show "3 9" but doesn't
    return 0;
    }

    When I reverse the order in which members foo and bar are declared, the
    program works properly. It appears as though members are initialised in the
    order in which they are declared. Is this gaurenteed? Thanks.
     
    Jason Heyes, Dec 3, 2003
    #1
    1. Advertising

  2. Jason Heyes wrote:
    > It appears as though members are initialised in the order in which they
    > are declared. Is this gaurenteed?


    Yes, it is. See section 12.6.2/5 of the C++ standard.

    --
    Russell Hanneken

    Remove the 'g' from my address to send me mail.
     
    Russell Hanneken, Dec 3, 2003
    #2
    1. Advertising

  3. Jason Heyes

    Phlip Guest

    "Jason Heyes" <> wrote in message
    news:3fcd7f04$0$13968$...
    > Here is a program that doesn't work as intended:
    >
    > #include <iostream>
    > using namespace std;
    >
    > class MyClass
    > {
    > int bar;
    > int foo;
    > public:
    > MyClass(int foo_, int bar_) : foo(foo_), bar(bar_ + foo) { }
    > void print() const { cout << foo << " " << bar << endl; }
    > };
    >
    > int main()
    > {
    > MyClass obj(3,6);
    > obj.print(); // intended to show "3 9" but doesn't
    > return 0;
    > }
    >
    > When I reverse the order in which members foo and bar are declared, the
    > program works properly. It appears as though members are initialised in

    the
    > order in which they are declared. Is this gaurenteed? Thanks.


    The rule of thumb: The compiler tries to ensure everything destructs in
    order reversed from their construction. (You should match this rule among
    allocations and de-allocations.)

    Hence, everything constructs in a known, defined order. Inside one class,
    this order is top-to-bottom for each members.

    That's why lint programs will complain when members appear to construct out
    of order.

    Even if you obey that rule, and construct bar before foo, don't abuse your
    knowledge of the mechanics to indulge in prank construction arguments.
    Reading a member inside another member's constructor should be well outside
    everyone's "sane subset".

    Further, the code above causes undefined behavior. 'bar(bar_ + foo)' reads
    the member 'foo' before it has constructed. In practice this only means
    'foo' returns garbage, but in theory "undefined behavior" means anything
    could happen. For example, if 'foo' somehow occupied memory not yet switched
    by hardware to permit reads, the program would crash.

    --
    Phlip
     
    Phlip, Dec 3, 2003
    #3
  4. Jason Heyes

    Dan W. Guest

    On Wed, 3 Dec 2003 17:13:23 +1100, "Jason Heyes"
    <> wrote:

    >Here is a program that doesn't work as intended:
    >
    >#include <iostream>
    >using namespace std;
    >
    >class MyClass
    >{
    > int bar;
    > int foo;
    >public:
    > MyClass(int foo_, int bar_) : foo(foo_), bar(bar_ + foo) { }
    > void print() const { cout << foo << " " << bar << endl; }
    >};
    >
    >int main()
    >{
    > MyClass obj(3,6);
    > obj.print(); // intended to show "3 9" but doesn't
    > return 0;
    >}
    >
    >When I reverse the order in which members foo and bar are declared, the
    >program works properly. It appears as though members are initialised in the
    >order in which they are declared. Is this gaurenteed? Thanks.
    >


    Yup, order of member declaration in the class, not in the
    constructor's initializer list. I think you can violate it by moving
    the initializations into the body of the constructor, not that it
    would be a good idea.
     
    Dan W., Dec 3, 2003
    #4
  5. "Dan W." <> schrieb im Newsbeitrag
    news:...
    > On Wed, 3 Dec 2003 17:13:23 +1100, "Jason Heyes"
    > <> wrote:
    >
    > >Here is a program that doesn't work as intended:
    > >
    > >#include <iostream>
    > >using namespace std;
    > >
    > >class MyClass
    > >{
    > > int bar;
    > > int foo;
    > >public:
    > > MyClass(int foo_, int bar_) : foo(foo_), bar(bar_ + foo) { }
    > > void print() const { cout << foo << " " << bar << endl; }
    > >};
    > >
    > >int main()
    > >{
    > > MyClass obj(3,6);
    > > obj.print(); // intended to show "3 9" but doesn't
    > > return 0;
    > >}
    > >
    > >When I reverse the order in which members foo and bar are declared, the
    > >program works properly. It appears as though members are initialised in

    the
    > >order in which they are declared. Is this gaurenteed? Thanks.
    > >

    >
    > Yup, order of member declaration in the class, not in the
    > constructor's initializer list. I think you can violate it by moving
    > the initializations into the body of the constructor, not that it
    > would be a good idea.


    No, that would be assignements. The initialization would still happen in the
    order
    of the declaration. But I think it would be a good idea, at least in the
    case of ints
    to assign them in the body rather than relying on the order of the members
    in the
    header file. But even better would be:
    class MyClass {
    ....
    MyClass(int foo_, int bar_) : foo(foo_), bar(bar_ + foo_) { } // note
    the underscore in foo_

    CU
    Norbert
     
    Norbert Riedlin, Dec 3, 2003
    #5
  6. Jason Heyes

    Jason Heyes Guest

    "Norbert Riedlin" <> wrote in message
    news:bql2uh$240u16$-berlin.de...
    >
    > No, that would be assignements. The initialization would still happen in

    the
    > order
    > of the declaration. But I think it would be a good idea, at least in the
    > case of ints
    > to assign them in the body rather than relying on the order of the members
    > in the
    > header file. But even better would be:
    > class MyClass {
    > ...
    > MyClass(int foo_, int bar_) : foo(foo_), bar(bar_ + foo_) { } // note
    > the underscore in foo_
    >
    > CU
    > Norbert


    Yes that is better. What happens when members have non-trivial
    initialisation? Here is an example:

    class MyClass
    {
    Bar bar; // Bar has constructor with a Foo object parameter
    Foo foo;

    Foo make_foo(std::string name); // involves some serious work

    public:
    MyClass(std::string name) : foo(make_foo(name)), bar(foo) { }
    };

    I could write

    MyClass(std::string name) : foo(make_foo(name)), bar(make_foo(name)) { }

    but that involves calling make_foo twice. I think I'd better declare the
    members in the right order instead.
     
    Jason Heyes, Dec 3, 2003
    #6
  7. >
    > Yes that is better. What happens when members have non-trivial
    > initialisation? Here is an example:
    >
    > class MyClass
    > {
    > Bar bar; // Bar has constructor with a Foo object parameter
    > Foo foo;
    >
    > Foo make_foo(std::string name); // involves some serious work
    >
    > public:
    > MyClass(std::string name) : foo(make_foo(name)), bar(foo) { }
    > };
    >
    > I could write
    >
    > MyClass(std::string name) : foo(make_foo(name)), bar(make_foo(name))

    { }
    >
    > but that involves calling make_foo twice. I think I'd better declare the
    > members in the right order instead.
    >


    You are right. Always declare the members in the right order, but don't
    depend on it.
    You might be faced with classes written by other developers or with your
    classes
    changed by other developers. Some compilers warn about not following the
    guideline,
    but others don't.

    Your make_foo() function is a good starter, but, as you mustnot depend on
    non-statics
    in MyClass, make make_foo() static if possible. (And think twice, if it's
    not possible)
    Making make_foo() a non-member might even be the option I would choose (And
    of
    course, change the type of the parameter to const std::string&)

    Hm... While thinking about this "calling make_foo() twice" in your example,
    I thought
    about the following "sollution":

    Foo make_foo(const std::string& name) {
    //...
    }

    class MyClass {
    Bar bar; // Bar has constructor with a const Foo& parameter
    Foo foo;

    MyClass(const std::string& name) : bar(foo(make_foo(name))) { }
    }

    I think this is highly undefined behaviour, because there are two
    contradicting concepts:
    1. parameters are evaluated, before a call is actually performed
    2. members are initialized from top to bottom

    As of rule 1, first make_foo() should be called, which in turn is used to
    initialize foo, which in
    turn is used to initialize bar. // note: foo initialized before bar!
    Rule two mandates, that bar must be initialized with foo, then foo will be
    initialized.

    What is happenig here?

    TIA
    Norbert
     
    Norbert Riedlin, Dec 4, 2003
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Samuele Armondi
    Replies:
    4
    Views:
    1,033
    Victor Bazarov
    Jun 25, 2003
  2. Tim
    Replies:
    2
    Views:
    1,509
  3. santosh

    member const initialisation

    santosh, May 23, 2005, in forum: C++
    Replies:
    3
    Views:
    433
    Lionel B
    May 23, 2005
  4. Tim Clacy
    Replies:
    8
    Views:
    396
    Tim Clacy
    May 30, 2006
  5. DL

    Ordered list inside ordered list

    DL, Nov 9, 2009, in forum: Javascript
    Replies:
    6
    Views:
    329
    Dr J R Stockton
    Nov 21, 2009
Loading...

Share This Page