2013-05-01 30 views
4

我發現了一段代碼,它有UB,並被告知將它留在代碼中,並且有一條評論指出它是UB。僅使用MSVC2012。未定義的行爲是否存在有效的「用例」?

代碼本身具有Foo對象的原始陣列,然後投射該數組char*reinterpret_cast<char*>,然後在其上調用delete casted_array(這樣,不刪除[])。

像這樣:

Foo* foos = new Foo[500]; 

char* CastedFoos = reinterpret_cast<char*>(foos); 

delete CastedFoos; 

按標準5.3.5/3這顯然是未定義的行爲。

很明顯,這段代碼做的是避免必須調用析構函數作爲優化。

我想知道,有沒有實際的地方在代碼離開UB可以考慮有效

另外,就我而言,在代碼中留下上述內容並不聰明,對嗎?

+0

技術上,UB是UB。一個特定的實現可能會演示UB類型的特定循環行爲,但這不會保證行爲不可移植。 – 2013-05-01 12:57:00

+1

幾乎所有實現特定的擴展在標準C++中都是未定義的行爲,並且使用擴展肯定有合法的原因。 – hvd 2013-05-01 12:57:38

+2

沒關係這個問題,但爲什麼這是必要的?如果'Foo'沒有任何有用的破壞,就給它一個微不足道的析構函數。如果它確實存在,並且你接受了泄漏,那麼爲什麼還要放開記憶呢? – 2013-05-01 13:07:09

回答

6

的C++標準定義如下「未定義行爲」:

行爲此標準並沒有規定要求

所以,如果你想你的代碼移植到不同的編譯器和平臺,那麼你的代碼不應該依賴未定義的行爲,因爲在這些情況下做的程序(由不同的編譯器編譯你的代碼產生的)可能會有所不同。

如果你不關心可移植性,那麼你應該檢查你的編譯器是否記錄了它在感興趣的情況下的行爲。如果它沒有記錄它的功能(並且不需要),請注意,編譯器可以在不同版本之間進行警告的情況下更改它的功能。還要注意其行爲可能是非確定性的。因此,例如,它可能會在1%的時間內崩潰,在臨時測試中您可能不會注意到它,但稍後它會在您投入生產時回來並咬你。所以,即使你使用的是一個編譯器,依靠未定義的行爲仍然是一個壞主意。

關於你的具體的例子,你可以重寫它來達到同樣的效果(不調用析構函數,但回收內存),在不導致不確定的行爲方式。分配std::aligned_storage持有富陣列,叫安置新構建富陣列上的aligned_storage,那麼當要解除數組,解除分配aligned_storage無需調用位置刪除。

當然,這仍然是一個可怕的設計,可能會導致內存泄漏或其他問題取決於什麼Foo::~Foo()應該這樣做,但至少它不是UB。

+0

謝謝你,這是有道理的。 – 2013-05-01 14:48:45

7

這完全取決於你的觀點。

舉一個極端的例子:在C++ 03中,線程是未定義的行爲。只要有多個線程,程序的行爲就不再由C++標準定義。

然而,大多數人會說線程是有用

當然,根據C++標準,多線程可能是UB,但個別編譯器並不會將視爲未定義。他們提供了一個額外的保證,多線程將按照您的預期,工作。在抽象中談到C++時,UB沒有任何用處。它怎麼可能?你不知道會發生什麼或會發生什麼。

但是在特定的應用程序中,由特定編譯器編譯以在特定操作系統上運行的特定代碼,有時可能知道一塊UB是安全的,並且(2)最終具有某種有益效果。

相關問題