首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >不调用单例构造函数

不调用单例构造函数
EN

Stack Overflow用户
提问于 2018-12-18 22:03:04
回答 2查看 499关注 0票数 13

我有单例类,用于在一个线程(GUI线程)中使用,以防止错误使用,我添加了assert

代码语言:javascript
复制
//header file
class ImageCache final {
public:
    ImageCache(const ImageCache &) = delete;
    ImageCache &operator=(const ImageCache &) = delete;
    static ImageCache &instance()
    {
       static ImageCache cache;
       return cache;
    }
    void f();
private:
    QThread *create_context_ = nullptr;
    ImageCache();
};

//cpp
ImageCache::ImageCache()
{
    create_context_ = QThread::currentThread();
    qInfo("begin, cur thread %p\n", create_context_);
}

void ImageCache::f()
{
    assert(create_context_ == QThread::currentThread());
}

所有操作都很好,但是在一台机器上,ImageCache::f中有断言失败,我没有直接访问该机器的权限(因此出现了这个问题)。

有趣的是,根据日志,ImageCache::ImageCache根本没有被调用,断言失败是因为

assert(0 == QThread::currentThread());

我将ImageCache::instance的实现从头文件移到.cpp文件,将更新的源代码发送给这些机器的用户(在我的所有工作都很好),他重新构建和所有开始工作如预期。

我要求他提供编译后的二进制文件(包含断言失败和没有断言),它们之间唯一的区别是ImageCache::instance实现的位置,

比较汇编程序。

ImageInstance::instance().f()的调用完全没有区别,ImageInstance::instance的反汇编程序也有一个不同之处,

失败的例子是这样的:

代码语言:javascript
复制
 static ImageCache &instance()
   4938f:   55                      push   %rbp
   49390:   48 89 e5                mov    %rsp,%rbp
   49393:   41 54                   push   %r12
   49395:   53                      push   %rbx
    {
        static ImageCache cache;
   49396:   48 8b 05 bb db 23 00    mov    0x23dbbb(%rip),%rax        # 286f58 <_ZGVZN10ImageCache8instanceEvE5cache@@Base-0x2150>
   4939d:   0f b6 00                movzbl (%rax),%eax
   493a0:   84 c0                   test   %al,%al
   493a2:   0f 94 c0                sete   %al
   493a5:   84 c0                   test   %al,%al
   493a7:   74 5c                   je     49405 <_ZN10ImageCache8instanceEv+0x76>
   493a9:   48 8b 05 a8 db 23 00    mov    0x23dba8(%rip),%rax        # 286f58 <_ZGVZN10ImageCache8instanceEvE5cache@@Base-0x2150>
   493b0:   48 89 c7                mov    %rax,%rdi
   493b3:   e8 08 b7 fe ff          callq  34ac0 <__cxa_guard_acquire@plt>

好的地方是这样的:

代码语言:javascript
复制
ImageCache &ImageCache::instance()
{
   50c12:   55                      push   %rbp
   50c13:   48 89 e5                mov    %rsp,%rbp
   50c16:   41 54                   push   %r12
   50c18:   53                      push   %rbx
    static ImageCache cache;
   50c19:   0f b6 05 98 94 23 00    movzbl 0x239498(%rip),%eax        # 28a0b8 <_ZGVZN10ImageCache8instanceEvE5cache>
   50c20:   84 c0                   test   %al,%al
   50c22:   0f 94 c0                sete   %al
   50c25:   84 c0                   test   %al,%al
   50c27:   74 50                   je     50c79 <_ZN10ImageCache8instanceEv+0x67>
   50c29:   48 8d 3d 88 94 23 00    lea    0x239488(%rip),%rdi        # 28a0b8 <_ZGVZN10ImageCache8instanceEvE5cache>
   50c30:   e8 cb 3d fe ff          callq  34a00 <__cxa_guard_acquire@plt>

不同的是

代码语言:javascript
复制
//bad
mov    0x23dbbb(%rip),%rax 
movzbl (%rax),%eax
//good
movzbl 0x239498(%rip),%eax

我的解释是,由于某种原因,来自第一个变量的%eax寄存器得到了错误的值,因此决定了全局对象是初始化的,而不是初始化的。在第二种情况下,所有操作都如预期的那样工作。

那么是编译器失败(gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0 / amd64 / linux)还是我应该使用ImageCache::.cpp中的实例是因为某种原因,或者是其他一些导致不同代码生成的原因,比如一些编译器falgs可能会导致这个失败?代码是用-O0 -std=c++11和其他一些标志编译的,cmake在编译具有Qt库依赖性的共享库时会自动添加。

另外,我要求使用fprintf(stderr而不是qInfo的测试代码,用户看到第二种情况下的输出,在第一种情况下没有输出。

EN

回答 2

Stack Overflow用户

发布于 2018-12-19 01:09:28

原始答案

据我所知,头文件中函数的问题是,您可以获得多个定义,而行为却未指定。

本质上,编译器可能会生成多个函数( instance ),每个编译单元都有一个包含该标头的函数,因此,如果它们在链接时没有合并/消除,那么每个函数都会有自己的变量。

在windows中,如果在多个DLL中编译相同的代码,在每个动态库中重复一些变量,我们可能会遇到类似的问题。

然后会发生的情况是,由于每个客户端都有自己的副本,另一个客户端在另一个翻译单元(您的问题)或另一个DLL (我的问题)中没有看到由一个客户所做的更改。

通过将定义移动到源文件,您将得到一个单独的定义,从而避免这个问题。

在C++中,如果不遵循规范,通常会得到未定义的行为。这取决于程序员知道他在做什么。

更新

正如所指出的,根据目前的标准,我的假设可能是错误的。因此,问题还可能是过时的编译器编译器错误

对正在发生的情况的可能解释:

在许多情况下,当编译器合并重复项时,代码将是相同的,因此它将不会对所选择的代码产生任何影响。在这里,假设编译器为静态变量分配两个不同的地址(每个编译单元一个),并且以某种方式内联对instance()的调用,使其使用原始变量而不是合并的变量,这可能会解释所观察到的行为。

票数 1
EN

Stack Overflow用户

发布于 2019-03-13 19:19:31

看起来是QThread::currentThread()的一个问题。它使用的是一个模块本地静态对象-我猜。如果是这样的话,模块与用户代码的链接顺序会导致行为差异。你试过不同版本的QT吗?我想这个版本的Qt有一个过时的设计--不使用现代成语,比如,甚至旧的漂亮的计数器技巧。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53841757

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档