我有单例类,旨在用于一个线程(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
的测试代码,
用户在第二种情况下看到输出,而在第一种情况下看不到输出。
答案 0 :(得分:0)
看起来像switch
的问题。我使用的是模块本地静态对象。如果是这种情况,则模块与用户代码的链接顺序会导致行为差异。您尝试过其他版本的QT吗?
我猜这个版本的Qt具有过时的设计-不使用像Meyer singleton这样的现代习语,甚至不使用旧的漂亮的反欺骗。
答案 1 :(得分:-1)
原始答案
据我了解,头文件中具有此功能的问题是您可以获取多个定义,然后行为未指定。
本质上,编译器可能会生成多个函数instance
,每个包含该标头的编译单元都会生成一个函数,因此,如果在链接时未合并/消除它们,则每个函数都有自己的变量。>
在Windows中,如果我们在多个DLL中编译相同的代码(其中每个动态库中的某些变量重复),我们可能会遇到类似的问题。
然后会发生的情况是,由于每个客户端都有自己的副本,因此另一个客户端在另一个翻译单元(您的问题)或另一个DLL(我的问题)中看不到一个人所做的更改。
通过将定义移至源文件,您将获得一个定义,从而避免了问题。
在C ++中,如果不遵循规范,则通常会得到未定义的行为。程序员要知道他在做什么。
更新
如前所述,根据当前标准,我的评论可能是错误的。因此问题可能是过时的编译器或编译器错误。
可能发生的情况的解释:
在许多情况下,当编译器合并重复项时,代码将是相同的,因此选择哪一个不会造成任何区别。在这里,假设编译器为静态变量分配了2个不同的地址(每个编译单元一个地址),并且以某种方式内联对instance()
的调用,即使用原始变量而不是合并(选定)的地址,它可以解释观察到的行为。