Can not understand weird program behavior - hopefully someone can explain.
dummy.h:
#ifndef DUMMY_H
#define DUMMY_H
#include <iostream>
class Dummy
{
int val;
public:
int Init(int new_val)
{
return val = new_val;
}
int Get()
{
return val;
}
Dummy():
val(-1)
{
std::cout << "constructed" << std::endl;
}
~Dummy()
{
std::cout << "deconstructed" << std::endl;
}
};
#endif /*DUMMY_H*/
header.h:
#include "dummy.h"
extern Dummy dummy;
dummy.cpp:
#include "dummy.h"
Dummy dummy;
main.cpp:
#include <iostream>
#include "header.h"
int res1 = dummy.Init(2);
int res2 = dummy.Get();
int main()
{
std::cout << res1 << std::endl;
std::cout << res2 << std::endl;
std::cout << dummy.Get() << std::endl;
return 0;
}
compiling: g++ -Wall -Wextra main.cpp dummy.cpp
output:
constructed
2
2
-1
deconstructed
why the 2nd Get(), called in main() function returns -1? Why the assigned value disappears from the dummy instance and no deconstructor was called. How it becomes -1?
upd: added debug info to Init() and Get():
new output:
init
get
constructed
2
2
get
-1
deconstructed
upd2: funny fact - separate compiling and linking object files in one executable changed the situation:
g++ -c dummy.cpp
g++ -c main.cpp
g++ dummy.o main.o
./a.out
constructed
init
get
2
2
get
2
deconstructed
but IT IS A TRAP!
The order of initialization of global variables in different translation units is unspecified. Either dummy
or the two global int
s in main.cpp could be constructed first.
In your case, it's res1
and res2
that got initialized first, so you invoked undefined behaviour by calling member functions on object that wasn't yet constructed.
To fix it, try this:
header.h:
#include "dummy.h"
Dummy& getDummy();
dummy.cpp:
#include "dummy.h"
Dummy& getDummy()
{
static Dummy dummy; // gets initialized at first call
// and persists for the duration of the program
return dummy;
}
main.cpp
int res1 = getDummy().Init(2);
int res2 = getDummy().Get();
If it helps understanding, add some debug cout statements to Get
and Init
, too.
What you experienced is also known as static initialization order fiasco.
So, what happens is that int res1 = dummy.Init(2);
and int res2 = dummy.Get();
is run BEFORE dummy
has been initialized. Then, before main
is entered, dummy
gets constructed and has the value -1
stored in val
.
Yyou can possibly change this in this particular case, for this version of the compiler, by rearranging your object files to g++ -o myprog dummy.o main.o
instead of g++ -o myprog main.o dummy.o
- but that will not GUARANTEE to fix the problem, and in a future version of compiler/linker, the result may well alter again. The C++ standard gives no guarantee at all - I'm just suggesting the "order may matter" from what I've personally seen. And since there is no particular requirement for these things, the compiler vendor is allowed to change it in any which way they like at any time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With