[英]Singleton pattern: different behavior of auto_ptr and unique_ptr
在实现工厂类时,我遇到了std::auto_ptr
的行为,我无法理解。 我将问题减少到下面的小程序,所以......让我们开始吧。
考虑以下单例类:
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_;
这里使用智能指针来管理资源主要取决于在程序退出时避免泄漏的需要。 我在以下程序中使用此代码:
啊
#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;
}
现在有趣的部分。 我分别编译了三个来源:
$ g++ -I./ singleton.cpp -c
$ g++ -I./ a.cpp -c
$ g++ -I./ main.cpp -c
如果我按此顺序显式链接它们:
$ g++ main.o singleton.o a.o
一切都按照我的预期工作,我得到以下内容到stdout:
singleton::get()
0x804a0d4
CREATED
singleton::get()
DELETED
相反,我使用此顺序链接源:
$ g++ a.o main.o singleton.o
我得到这个输出:
singleton::get()
0x804a0dc
CREATED
singleton::get()
0x804a0dc
CREATED
DELETED
我尝试了不同的编译器品牌(Intel和GNU)和版本,这种行为在它们之间是一致的。 无论如何,我无法看到其行为取决于链接顺序的代码。
此外,如果auto_ptr
被unique_ptr
替换,则行为总是与我期望的正确行为一致。
这让我想到了一个问题: 有没有人知道这里发生了什么?
构造dummy
和std::auto_ptr< singleton > singleton::ptr_(0)
是未指定的。
对于auto_ptr
情况,如果构造dummy
然后是singleton::ptr_(0)
,则dummy
调用中创建的值将被ptr_(0)
的构造函数擦除。
我会通过ptr_(([](){ std::cout << "made ptr_\\n"; }(),0));
添加跟踪到ptr_
的构造ptr_(([](){ std::cout << "made ptr_\\n"; }(),0));
或类似的东西。
它与unique_ptr
工作的事实是巧合的,并且可能是由于优化,即unique_ptr(0)
可以判断它是零,因为这样做什么都没有( static
数据在构造开始之前被归零,所以如果编译器可以找出unique_ptr(0)
只是将内存归零,它可以合法地跳过构造函数,这意味着你不再将内存归零)。
解决此问题的一种方法是使用一种在使用前保证构造的方法,例如:
static std::auto_ptr< singleton >& get_ptr() {
static std::auto_ptr< singleton > ptr_(0);
return ptr_;
}
并用get_ptr()
替换对ptr_
引用。
未指定在不同翻译单元中定义的文件范围对象的构造顺序。 然而,通常,在第二翻译单元中定义的对象之前构造在另一翻译单元之前链接的翻译单元中定义的对象。 这里的区别在于ao
和singleton.o
的链接顺序。 当singleton.o
在ao
之前链接时, singleton::ptr_
在dummy
之前被初始化并且一切都很好。 当ao
首先被链接时, dummy
首先被初始化,这构造了单例; 然后将singleton::ptr_
初始化为0,抛弃指向singleton
原始副本的指针。 然后在调用foo
,对singleton::get()
的调用再次构造单例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.