8

I was looking for some tests to improve my C++ knowledge. Here is one of the exercises: What is the output of the following program?

#include <iostream>

class A
{
public:
    A(int n = 0) : m_i(n)
    {
        std::cout << m_i;
        ++m_i;
    }

protected:
    int m_i;
};

class B : public A
{
public:
    B(int n = 5) : m_a(new A[2]), m_x(++m_i) { std::cout << m_i; }

    ~B() { delete [] m_a; }

private:
    A m_x;
    A *m_a;
};

int main()
{
    B b;
    std::cout << std::endl;
    return 0;
}

Well, I tried this code, and the answer is 02002. I come here to have some explanation because I don't understand why 02002 is the result. I will explain my reasoning, but could some tell me where am I wrong please?

Let's call "str" the current string to print. When the b object is built:

  1. The constructor of A is called. str => 0, m_i => 1
  2. Construction of m_a(new A[2]). str => 000
  3. Construction of m_x(++m_i). str => 0002, m_i => 3
  4. Last update of str (in B's constructor) => str => 00023

Here are my questions:

  • Why is the final value of m_i 2 and not 3?
  • Why is the construction of m_x(++m_i) done before the one of m_a(new A[2])? I tried to exchange the position of the initialization of m_x and m_a and the answer is still the same : 02002.
LogicStuff
  • 19,397
  • 6
  • 54
  • 74
Ptiseb
  • 795
  • 1
  • 7
  • 19
  • Since `m_x` is declared *before* `m_a` in the `B` class, `B(int n = 5) : m_a(new A[2]), m_x(++m_i)` is ill-formed and should be `B(int n = 5) : m_x(++m_i), m_a(new A[2])`. – YSC Dec 23 '15 at 16:19
  • @YSC is correct, at least, you will get a ton of warning when compile with gcc (I'm not sure about the others) – Danh Dec 23 '15 at 16:20
  • @YSC `B(int n = 5) : m_a(new A[2]), m_x(++m_i)` is not ill-formed. Perhaps it should be made ill-formed, but unfortunately it isn't. – cpplearner Dec 23 '15 at 16:22
  • you're right, with warnings i get: "test.cpp:27: warning: ‘B::m_a’ will be initialized after" – Ptiseb Dec 23 '15 at 16:35

2 Answers2

12

Why is the final value of m_i 2 and not 3?

Because new A[2] creates two separate instances having nothing to do with *this. m_i of the b instance is only incremented in A::A and B::B (twice).

If incrementation of m_i should be performed on the same instance (m_i being a reference, for example), it would be more reasonable to think that the final value of m_i should be 4 (there are two objects in the array - two additional increments).

Why is the construction of m_x(++m_i) done before the one of m_a(new A[2])?

Because the order of initialization depends on the order of declaration of the data members, not the order in which you write initializations in the member initializer list.

Community
  • 1
  • 1
LogicStuff
  • 19,397
  • 6
  • 54
  • 74
  • Thx for the answer ! I just understood why 2 and not 3. i also add one more incrementation during the construction of m_x. – Ptiseb Dec 23 '15 at 16:24
  • @Ptiseb Some compilers (MingW and VC) issues warnings if you initialize members in different order than you declared them. – Resurrection Jun 26 '16 at 16:37
4

When you construct b the A part of B is constructed first. This gives the 0 in the output. Then we get the 2 because m_x(++m_i) happens first since m_x is listed first in the class. Since m_i is 1 from the A part of B being constructed ++m_i gives 2 and now we have 02. Then m_a(new A[2]) is ran which gives us 2 0(one for each memeber of the array). That puts us at 0200. We then get the final 2 from { std::cout << m_i; } since m_i is still 2 from m_x(++m_i).

NathanOliver
  • 171,901
  • 28
  • 288
  • 402