2014-11-01 104 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