crash: wrong declaration order

Hi!

I recently ran into a crash, where I had declared member objects of a class in the wrong order. Basically, member A was using member B, but member B was declared after member A. So, when member A was trying to use member B, that did not work, cause member B was not yet constructed.

My question is: how can I prevent this? My compiler did not flag this. Neither XCode nor Visual Studio warned me.
Is it possible to make the compiler aware of this? Or is it possible to use a certain code pattern, so that this problem cannot happen?

Its probably best, if I post example code, so you understand my case better:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Foo
{
public:
    Foo()
    {
        data = new int();
    }
    
    ~Foo()
    {
        delete data;
    }
    
    void setData(int newValue)
    {
        *data = newValue;
    }
    
private:
    
    //a pointer to an int:
    int* data = nullptr;
};

class Bar
{
public:
    Bar(Foo& foo)
    {
        foo.setData(42);
    }
};

class CrashTest
{
public:
    CrashTest() : bar(foo)
    {
        
    }
    
private:
    Bar bar;
    Foo foo;
    
};

main()
{
    CrashTest crashTest;
}


So here, the class CrashTest is declaring two class members: bar and foo. bar is declared first. BUT: bar is using foo. Cause in the constructor of bar, it requires a Foo object. Now, when bar tries to access foo, the constructor of foo has not yet run. That means, it has not initialised its data. And thus the access from bar to foo is invalid. And it crashes.

One solution is, to reverse the order of declaration in CrashTest. If I declare foo first, and bar seconds, things are fine.

So this is obviously a bug of mine! But I would like a way to prevent this in the future.
Can I somehow make the compiler aware of this so that it generates a warning? Cause currently it does not warn about it.
Or can I use some kind of code pattern, which prevents me from making this mistake ever again? :-)

Curious to see what people here think :-)
Look for forward declaration. That can help you. Less or more is the concept.
Swap the order of Foo foo; and Bar bar;
They are constructed in the order they are listed.
Last edited on
Thanks for your answers.

@enriquemesa8080:
I don't think forward declaration is going to help here? Cause it compiles fine. It crashes during run-time.

@dutch:
Yes, I know that I can do that to solve my problem. But I want the compiler to warn me. Cause its so easy to miss in a larger code base. Or I would like to use a code pattern, which somehow prohibits me to make that mistake in the first place.
1
2
3
4
5
6
    CrashTest() :
        foo(), //warning: field 'foo' will be initialized after field 'bar' [-Wreorder]
        bar(foo)
    {

    }


sadly
1
2
3
private:
    Bar bar;
    Foo foo{}; //no warning 



> Or is it possible to use a certain code pattern, so that this problem cannot happen?
http://www.cs.technion.ac.il/users/yechiel/c++-faq/using-this-in-ctors.html
don't pass any data member from the this object (...) to any other data member's initializer!
its so easy to miss in a larger code base.

True, although the issue is within one class:
1
2
3
4
5
6
7
8
9
10
11
12
class CrashTest
{
public:
    CrashTest()
    : bar( foo ) // error: initializer uses uninitialized value
    // , foo() // foo initializes here
    {}
    
private:
    Bar bar; // order of members dictated here
    Foo foo;
};

Yes, a class can have many bases and members, and many constructors too.
If that is too much to be diligent about, ...
Thanks again for your answers.

I am thinking about making my members a shared_ptr. That way the decleration order in the class would not matter.
In other words:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class CrashTest
{
public:
    CrashTest()
    {
         foo = std::make_shared<Foo>();
         bar = std::make_shared<Bar>(foo);   
    }
    
private:
    std::shared_ptr<Bar> bar;
    std::shared_ptr<Foo> foo;
    
};


What do you think?
- wasteful
- ¿why shared instead of unique?
- std::make_shared<Bar>(foo); ¿now Bar constructor wants a shared pointer? ¿why a reference is no longer good enough?
- I guess that you now have a guaranteed* runtime error if you screw up the constructor. Sadly, it's a runtime error.

*nope, undefined behaviour
Last edited on
Enable compiler warnings.

If you are using GCC or a GCC-compatible compiler driver, pass the flag -Wreorder to your compiler.
https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect-Options.html
Preferably, enable all of the most useful diagnostics and ask for strict conformance with the flags -Wall -Wextra -pedantic-errors

If you are using MSVC or a MSVC-compatible compiler driver, pass the flag /W15038 to your compiler.
https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/c5038?view=vs-2019
Preferably, enable all of the most useful diagnostics with the flag /W4
Last edited on
Alatar wrote:
What do you think?

It you feel like abandoning member initializer lists in favour of assigning in the constructor body, then in your code the order doesn’t matter any more. I mean, once you get inside the constructor body, as far as I know all properties have already been fully initialized.
I.e. you don’t even need any kind of pointers.

Example (sorry for the cramped style):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Foo {
public:
    Foo() : data { new int } {}
    ~Foo() { delete data; }
    void setData(int newValue) { *data = newValue; }

private:
    int* data { nullptr };
};


class Bar {
public:
    Bar() = default;
    explicit Bar(Foo& foo_arg) { foo_arg.setData(42); }
    Bar& operator=(Foo& foo_arg) { foo_arg.setData(42); return *this; }
};


class CrashTest {
public:
    CrashTest() { bar = foo; }

private:
    Bar bar;
    Foo foo;
};


main()
{
    CrashTest crash_test;
}

Topic archived. No new replies allowed.