論壇版: https://forum.community.tw/t/topic/158
新設立的論壇,也可當作部落格的留言系統
歡迎大家來試試看
另外想問大家對於問答區有興趣嗎?
以下正文
這一篇是把之前自己遇到的問題記錄下來,雖然現在看來有點蠢,但當時還是花了一
些時間才找出來
經歷
在檢測單元測試時,發現有一項測試,在 CI/CD 中會隨機出錯,一開始想說這種隨機錯
誤通常是沒有初始化,或者一些陣列操作超出範圍,導致記憶體被意外修改了。在
linux 又很難重現,只有在 github action MSVC 中出錯,又當時沒有手邊沒有 MSVC 能
夠直接使用,導致一開始方向並沒有很明確,檢查了初始化跟陣列操作都也正確,最後才
發現某一項初始化在第一項測試不一定能夠歸零,但第二項之後都可以正確歸零,所以才
開始懷疑初始化的順序,然後在查了一些資料後確認 class non-static data (非靜態成
員) 初始化順序是根據宣告順序,而非直接使用 constructor 的順序
參考資料 https://en.cppreference.com/w/cpp/language/constructor
中的 Initialization order 的第三項 Then, non-static data member are
initialized in order of declaration in the class definition.
例子(論壇版有自動上色的範例)
```
#include <iostream>
class test {
public:
test(int val) : b(val), a(b) {}
// many codes
int a;
int b;
};
int main()
{
test k(3);
std::cout << k.a << " " << k.b << std::endl;
return 0;
}
```
以上是一個簡單的例子,在 constructor 後面 member initializers 是使用 b(val),
a(b),當 class 中間又有更多函式時,在看 constructor 時,是會看不到宣告順序的,
所以我當時就直覺認為會是 b 先被給定 val 然後 a 再被給定 b 也就是 val,然而這是
錯的。
想像輸出會是 3 3
實際情形
在前面有提過他是根據宣告順序,而非 member initializers 怎麼寫 (constructor 後
面 {} 之前),所以這邊實際的運作會是,a 被給定 b (未定) , b 再被給定 val,因
此 a 的值會根據 b 一開始為初始化的值而決定。
因此實際輸出會是 * 3
而在這邊 * 在 linux 為初始化地很常會是零 (個人經驗),又剛好我的初始化是要歸零
,導致在 linux 的部分很難遇到這樣的問題,幸好在 MSVC 有指出這樣的問題。
檢查
後來有分享這件事給同實驗室的,他們也給出了一項編譯器選項,可以指出這項問題
-Wreorder,就會指出例子中的 test::b 會在 test::a 之後初始化
https://imgur.com/pj3sOQR.png
可以很迅速地發現這項問題,也可以透過 https://godbolt.org/z/Edjc161sK 來看
結語
class non-static data member 在 member initializers 中初始化順序是根據宣告順序
來定,可以用 -Wreorder 來檢查,以上是之前遇到的問題分享。