While implementing a factory class I encountered a behavior of std::auto_ptr
that I am not able to understand. I reduced the problem down to the following small program, so ... let's start.
Consider the following singleton class:
singleton.h
#ifndef SINGLETON_H_
#define SINGLETON_H_
#include<iostream>
#include<memory>
class singleton {
public:
static singleton* get() {
std::cout << "singleton::get()" << std::endl;
if ( !ptr_.get() ) {
std::cout << &ptr_ << std::endl;
ptr_.reset( new singleton );
std::cout << "CREATED" << std::endl;
}
return ptr_.get();
}
~singleton(){
std::cout << "DELETED" << std::endl;
}
private:
singleton() {}
singleton(const singleton&){}
static std::auto_ptr< singleton > ptr_;
//static std::unique_ptr< singleton > ptr_;
};
#endif
singleton.cpp
#include<singleton.h>o
std::auto_ptr< singleton > singleton::ptr_(0);
//std::unique_ptr< singleton > singleton::ptr_;
Here the use of a smart pointer to manage the resource is mainly dictated by the need to avoid leaks at program exit. I use then this code in the following program:
a.h
#ifndef A_H_
#define A_H_
int foo();
#endif
a.cpp
#include<singleton.h>
namespace {
singleton * dummy( singleton::get() );
}
int foo() {
singleton * pt = singleton::get();
return 0;
}
main.cpp
#include<a.h>
int main() {
int a = foo();
return 0;
}
Now the funny part. I compile the three sources separately:
$ g++ -I./ singleton.cpp -c
$ g++ -I./ a.cpp -c
$ g++ -I./ main.cpp -c
If I link them explicitly in this order:
$ g++ main.o singleton.o a.o
everything work as I expect, and I get the following to stdout:
singleton::get()
0x804a0d4
CREATED
singleton::get()
DELETED
If instead I link the sources using this order:
$ g++ a.o main.o singleton.o
I get this output:
singleton::get()
0x804a0dc
CREATED
singleton::get()
0x804a0dc
CREATED
DELETED
I tried different compiler brands (Intel and GNU) and versions and this behavior is consistent among them. Anyhow, I am not able to see code whose behavior depends on the order of linking.
Furthermore, if auto_ptr
is substituted by unique_ptr
the behavior is ALWAYS consistent with what I expect to be the correct one.
That brings me to the question: Does anybody have a clue on what's going on here?
The order of construction of file-scope objects defined in different translation units is unspecified. However, typically, objects defined in a translation unit that is linked before another translation unit are constructed before objects defined in the second translation unit. The difference here is the order in which a.o
and singleton.o
are linked. When singleton.o
is linked before a.o
, singleton::ptr_
gets initialized before dummy
and all is well. When a.o
is linked first, dummy
gets initialized first, which constructs the singleton; then singleton::ptr_
gets initialized to 0, throwing away the pointer to the original copy of singleton
. Then in the call to foo
, the call to singleton::get()
constructs the singleton again.
The order at which dummy
and std::auto_ptr< singleton > singleton::ptr_(0)
is constructed is unspecified.
For the auto_ptr
case, if you construct dummy
then singleton::ptr_(0)
, the value created in the dummy
call is erased by the constructor of ptr_(0)
.
I would add tracking to the construction of ptr_
via ptr_(([](){ std::cout << "made ptr_\n"; }(),0));
or something like that.
The fact that it works with unique_ptr
is coincidental, and possibly due to optimizations whereby unique_ptr(0)
can figure out it is zeroed, as such does nothing (static
data is zeroed before construction starts, so if the compiler could figure out that unique_ptr(0)
just zeros the memory, it could legally skip the constructor, which means you no longer zero the memory).
One way to fix this is to use a method that guarantees construction before use, such as:
static std::auto_ptr< singleton >& get_ptr() {
static std::auto_ptr< singleton > ptr_(0);
return ptr_;
}
and replace references to ptr_
with get_ptr()
.
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