没有单例构造函数的调用

时间:2018-12-18 22:03:04

标签: c++ c++11 assembly

我有单例类,旨在用于一个线程(GUI线程), 为了防止错误使用,我添加了protected String doInBackground(String... String username = (String) arg0[0]; String password = (String) arg0[1]; String urlString = "https://splitfz.000webhostapp.com/login.php?username=" + username + "&password=" + password; StringBuffer chaine = new StringBuffer(""); URL url; HttpURLConnection urlConnection = null; try { url = new URL("https://splitfz.000webhostapp.com/login.php?username="+ username +"&password="+ password +""); urlConnection = (HttpURLConnection) url.openConnection(); connection.setRequestProperty("User-Agent", ""); connection.setRequestMethod("GET"); connection.setDoInput(true); connection.connect(); InputStream in = urlConnection.getInputStream(); InputStreamReader isw = new InputStreamReader(in); int data = isw.read(); while (data != -1) { char current = (char) data; data = isw.read(); System.out.print(current); } this.statusField.setText("deu certo"); } catch (Exception e) { this.statusField.setText("deu erro"); e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } } return chaine;

assert

一切正常,但是在一台机器上//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

好人是这样的

 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>

区别是

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>

我这样解释,由于某种原因,第一个变量的//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 :: instance, 还是其他导致差异代码生成的原因,例如某些编译器错误可能会导致此失败?代码是使用.cpp和其他一些cmake标志进行编译的,而这些标志在依赖于Qt库的共享库的编译时会自动添加。

我还要询问使用-O0 -std=c++11而不是fprintf(stderr的测试代码, 用户在第二种情况下看到输出,而在第一种情况下看不到输出。

2 个答案:

答案 0 :(得分:0)

看起来像switch的问题。我使用的是模块本地静态对象。如果是这种情况,则模块与用户代码的链接顺序会导致行为差异。您尝试过其他版本的QT吗? 我猜这个版本的Qt具有过时的设计-不使用像Meyer singleton这样的现代习语,甚至不使用旧的漂亮的反欺骗。

答案 1 :(得分:-1)

原始答案

据我了解,头文件中具有此功能的问题是您可以获取多个定义,然后行为未指定。

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

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

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

通过将定义移至源文件,您将获得一个定义,从而避免了问题。

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

更新

如前所述,根据当前标准,我的评论可能是错误的。因此问题可能是过时的编译器编译器错误

可能发生的情况的解释:

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