2014-11-01 89 views
0

这是我今天需要调试的简化代码片段。VS C++编译器应该如何处理这段代码?

我不小心输入是这样的:

QImage myImage = LoadImage(path); 
QImage scaledImage = myImage.scaled(100, 100); 
if (condition) 
{ 
    QImage scaledImage = scaledImage.mirrored(true, true); // *** Crash *** 
} 

代替intendend代码:

QImage myImage = LoadImage(path); 
QImage scaledImage = myImage.scaled(100, 100); 
if (condition) 
{ 
    scaledImage = scaledImage.mirrored(true, true); // *** Works fine *** 
} 

我本来期望的预期或将产生一个编译器错误的有缺陷的代码,要么工作。但它只是零分割而坠毁。为什么?有人能告诉我编译器对这个错误的观点吗?

+0

哪行代码失败?如果我怀疑这种“如果”情况的内在原因。没有理由为什么编译器会发出警告或错误,因为它是完全有效的代码。我能想象的唯一的错误是'if'语句中的'QImage'删除导致下一个作用域中'QImage'实例的资源相同。不要因为你得到的错误而动,但最终可能会导致它,因为你可能最终得到未定义的行为,这取决于'QImage'处理资源的程度。 – 2014-11-01 23:19:17

+0

我已经用评论“Crash”标出了这行(很容易错过我的猜测)。 – Silicomancer 2014-11-01 23:19:57

回答

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正在呼叫从左侧的初始化的对象上的功能。这是未定义的行为。

+0

那么C++标准不会将这种情况作为错误来处理?这太可怕了,为什么这不被视为错误? – Silicomancer 2014-11-01 23:34:25

+0

我不确切知道这个标准在什么地方被覆盖,但这被认为是未定义的行为。 – PeterT 2014-11-01 23:42:06

+1

@Silicomancer这个问题将帮助你的细节:http://stackoverflow.com/questions/9820027/using-newly-declared-variable-in-initialization-int-x-x1 – PeterT 2014-11-01 23:51:50

3

想想这行代码的作用:

QImage scaledImage = scaledImage.mirrored(true, true); // Crash 
  1. 符号scaledImage定义。此scaledImage符号名称将覆盖外部作用域中的同名名称。
  2. 调用mirrored()方法。
  3. scaledImage现在使用带有mirrored()输出的复制构造函数创建。

正如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'tor

换句话说,test()方法是calle d在任何构造函数被调用之前。那很糟!

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; 
    } 

}; 

现在任何东西之前程序崩溃可以打印出来:

但是,如果我们修改我们的例子中有一个数据成员它变得更糟。具体而言,在测试的第一行()是罪魁祸首:

data = "test"; 

如果你仔细想想,这是试图东西分配给尚未构建为QString。任何尝试访问或修改未构造对象的成员变量都是坏消息。

+0

@PeterT好点,我最初得到的操作顺序是错误的。我用一些有趣的测试案例更新了我的答案,以说明问题。谢谢! – MrEricSir 2014-11-02 00:04:38

+1

这甚至不会给你一个警告,即使用'-Wall -pedantic'也不是C++ grand:P? – PeterT 2014-11-02 00:08:25

+0

是的。令人惊讶的是,到2014年这个标准还没有被C++标准所固定。这是一种令人讨厌的行为。 – Silicomancer 2014-11-02 09:06:21