TW Community

class 非靜態成員初始化順序

the initialization order of class non-static data member is the order of declaration in the class.
這一篇其實是把自己之前遇到的問題記錄下來,雖然現在看來有點蠢,但當時還是花了一些時間才找出來

經歷

在檢測單元測試時,發現有一項測試,在 CI/CD 中會隨機出錯,一開始想說這種隨機錯誤通常是沒有初始化,或者一些陣列操作超出範圍,導致記憶體被意外修改了。在 linux 又很難重現,只有在 github action MSVC 中出錯,又當時沒有手邊沒有 MSVC 能夠直接使用,導致一開始方向並沒有很明確,檢查了初始化跟陣列操作都也正確,最後才發現某一項初始化在第一項測試不一定能夠歸零,但第二項之後都可以正確歸零,所以才開始懷疑初始化的順序,然後在查了一些資料後確認 class non-static data (非靜態成員) 初始化順序是根據宣告順序,而非直接使用 constructor 的順序

參考資料 cppreference 中的 Initialization order 的第三項 Then, non-static data member are initialized in order of declaration in the class definition.

完整的也貼在這邊方便參考

The order of member initializers in the list is irrelevant: the actual order of initialization is as follows:

  1. If the constructor is for the most-derived class, virtual bases are initialized in the order in which they appear in depth-first left-to-right traversal of the base class declarations (left-to-right refers to the appearance in base-specifier lists)
  2. Then, direct bases are initialized in left-to-right order as they appear in this class's base-specifier list
  3. Then, non-static data member are initialized in order of declaration in the class definition.
  4. Finally, the body of the constructor is executed

例子

#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 之後初始化

可以很迅速地發現這項問題,也可以透過此連結來看這個例子

結語

class non-static data member 在 member initializers 中初始化順序是根據宣告順序來定,可以用 -Wreorder 來檢查,以上是之前遇到的問題分享。

想詢問484寫錯了,< 變成 &lt; 了

1個讚

是,改成自動上色的格式時,沒改到,謝謝