这是我今天需要调试的简化代码段。
我意外地输入了这样的内容:
QImage myImage = LoadImage(path);
QImage scaledImage = myImage.scaled(100, 100);
if (condition)
{
QImage scaledImage = scaledImage.mirrored(true, true); // *** Crash ***
}
而不是意图代码:
QImage myImage = LoadImage(path);
QImage scaledImage = myImage.scaled(100, 100);
if (condition)
{
scaledImage = scaledImage.mirrored(true, true); // *** Works fine ***
}
我原本期望有缺陷的代码按预期工作或生成编译器错误。但它只是以零除法崩溃。为什么?有人能告诉我编译器关于这个错误的观点吗?
答案 0 :(得分:3)
想想这行代码的作用:
QImage scaledImage = scaledImage.mirrored(true, true); // Crash
scaledImage
。此scaledImage
符号名称将覆盖外部作用域中的同名名称。scaledImage
。正如PeterT在评论中指出的那样,这是未定义的行为:您在创建对象之前调用方法。在这种情况下,崩溃可以帮助您避免容易犯的错误。
以下是一个示例,详细说明了此问题的存在方式和原因:
class Tester {
public:
Tester() {
qDebug() << "Default c'tor";
}
Tester( const Tester& other ) {
qDebug() << "Copy c'tor";
}
Tester& Tester::operator=( const Tester& other ) {
qDebug() << "Assignment";
return *this;
}
Tester& test() {
data = "test";
return *this;
}
};
int main(int argc, char *argv[])
{
Q_UNUSED(argc);
Q_UNUSED(argv);
Tester test = test.test();
return 0;
}
该程序将输出以下内容:
测试
复制c&#39; tor
换句话说,在调用任何构造函数之前调用了test()方法。那太糟糕了!
但是如果我们用数据成员修改我们的例子会更糟糕:
class Tester {
QString data;
public:
Tester() {
qDebug() << "Default c'tor";
data = "data";
}
Tester( const Tester& other ) {
qDebug() << "Copy c'tor";
data = other.data;
}
Tester& Tester::operator=( const Tester& other ) {
qDebug() << "Assignment";
data = other.data;
return *this;
}
Tester& test() {
data = "test";
qDebug() << "Test";
return *this;
}
};
现在程序在打印出任何内容之前崩溃了。具体来说,test()中的第一行是罪魁祸首:
data = "test";
如果您考虑一下,这就是尝试将某些内容分配给尚未构建的QString。任何访问或修改未构造对象的成员变量的尝试都是坏消息。
答案 1 :(得分:1)
如果您减少此代码,原因仍然存在。
此:
QImage myImage = LoadImage(path);
QImage scaledImage = myImage.scaled(100, 100);
if (condition)
{
QImage scaledImage = scaledImage.mirrored(true, true); // *** Crash ***
}
与此有相同的错误:
QImage myImage = LoadImage(path);
QImage scaledImage = scaledImage.mirrored(true, true); // *** Crash ***
因为右侧的scaledImage
从左侧调用未初始化对象上的函数。这是未定义的行为。