1

I am reading into Scott Meyers book Effective c++. For constructors he recommends initialization of object data in a specific order.

  • Base class before derived class
  • Within a class data members initialized in the order which the are declared.

Appearantly not following these rules can lead to obscure behavioral bugs ... However he does'nt give any examples or go into details.

Can you give an example of what kind of bugs can occur?

Kwogggg
  • 13
  • 2
  • I think you will get compilation errors in the best case or on the worst case the compiler will do some conversion (if he finds the right constructor) to convert object X to object Y. and you dont want this behavior. g++ will do its best to success! and this can lead to massive runtime errors. trust Scott Meyers. he knows the job. see my answer below. – Adam May 13 '20 at 12:18
  • 4
    Does this answer your question? [What's the point of g++ -Wreorder?](https://stackoverflow.com/questions/1828037/whats-the-point-of-g-wreorder) – Rhathin May 13 '20 at 12:24
  • His recommendations reflect the order in which members WILL be initialised. Writing the constructor initialiser list so it *appears* the order is different causes confusion - and is not an error that a compiler is required to diagnose - particularly if one member or base is initialised using another that you assume (from the order in the initialiser list) is already initialised, but actually is not. Accessing members affected by that can cause undefined behaviour (e.g. if the result is a member or base being initialised using a member or base that is not yet initialised). – Peter May 13 '20 at 12:48
  • @Adam -- a compiler that refuses to compile code because the initializer list is in an order different from what the compiler expects does not conform to the language definition. – Pete Becker May 13 '20 at 14:47
  • Thank you @PeteBecker for the fix of my previous answer. – Adam May 13 '20 at 15:19

3 Answers3

5

For example,

struct S
{
   int a;
   int b;

   S() : b(42), a(b + 1) {}
};

leads to undefined behavior:

As contrary as we might expect from constructor, a is initialized before b (according to member order).

Jarod42
  • 203,559
  • 14
  • 181
  • 302
2

It can confusing to have the wrong order:

struct foo {
    int a;
    int b;
    foo(int x) : b(++x),a(++x) {
        std::cout << "a = " << a <<'\n';
        std::cout << "b = " << b <<'\n';
    }
};

Constructing a foo(1) prints:

a = 1
b = 2

Not a = 2, b = 1 as one might expect from the order of the initializer list. Real problems can occur if initialization of one member depends on another member. This is correct (but not nice):

struct bar {
    int a;
    int b;
    bar() : b(a),a(1){
        std::cout << "a = " << a <<'\n';
        std::cout << "b = " << b <<'\n';
    }
};

Prints:

a = 1
b = 1

And this invokes undefined behavior:

struct broken {
    int a;
    int b;
    broken() : b(1),a(b){
        std::cout << "a = " << a <<'\n';
        std::cout << "b = " << b <<'\n';
    }
};
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
1

Can you give an example of what kind of bugs can occur?

class foo 
{
   int *ptr;
   int size;
   public:
      foo() : size(10), ptr(new int[size]) {}
};

int main()
{
   foo f;
}

See the warnings here.

The size is initialized after the ptr is initialized, thus size is actually some random value when new is issued to allocate dynamically.

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45