Default, value and zero initialization mess

后端 未结 3 855
广开言路
广开言路 2020-11-28 02:38

I am very confused about value- & default- & zero-initialization. and especially when they kick in for the different standards C++03 and C++11 (and

3条回答
  •  猫巷女王i
    2020-11-28 02:47

    I can confirm that in C++11, everything mentioned in the question under C++14 is correct, at least as per compiler implementations.

    In order to verify this, I added the following code to my test suite. I tested with -std=c++11 -O3 in GCC 7.4.0, GCC 5.4.0, Clang 10.0.1, and VS 2017, and all the tests below pass.

    #include 
    #include 
    
    struct A { int m;                    };
    struct B { int m;            ~B(){}; };
    struct C { int m; C():m(){}; ~C(){}; };
    struct D { int m; D(){};             };
    struct E { int m; E() = default;     };
    struct F { int m; F();               }; F::F() = default;
    
    // We use this macro to fill stack memory with something else than 0.
    // Subsequent calls to EXPECT_NE(a.m, 0) are undefined behavior in theory, but
    // pass in practice, and help illustrate that `a.m` is indeed not initialized
    // to zero. Note that we initially tried the more aggressive test
    // EXPECT_EQ(a.m, 42), but it didn't pass on all compilers (a.m wasn't equal to
    // 42, but was still equal to some garbage value, not zero).
    //
    #define FILL { int m = 42; EXPECT_EQ(m, 42); }
    
    // We use this macro to fill heap memory with something else than 0, before
    // doing a placement new at that same exact location. Subsequent calls to
    // EXPECT_EQ(a->m, 42) are undefined behavior in theory, but pass in practice,
    // and help illustrate that `a->m` is indeed not initialized to zero.
    //
    #define FILLH(b) std::unique_ptr bp(new int(42)); int* b = bp.get(); EXPECT_EQ(*b, 42)
    
    TEST(TestZero, StackDefaultInitialization)
    {
        { FILL; A a; EXPECT_NE(a.m, 0); } // UB!
        { FILL; B a; EXPECT_NE(a.m, 0); } // UB!
        { FILL; C a; EXPECT_EQ(a.m, 0); }
        { FILL; D a; EXPECT_NE(a.m, 0); } // UB!
        { FILL; E a; EXPECT_NE(a.m, 0); } // UB!
        { FILL; F a; EXPECT_NE(a.m, 0); } // UB!
    }
    
    TEST(TestZero, StackValueInitialization)
    {
        { FILL; A a = A(); EXPECT_EQ(a.m, 0); }
        { FILL; B a = B(); EXPECT_EQ(a.m, 0); }
        { FILL; C a = C(); EXPECT_EQ(a.m, 0); }
        { FILL; D a = D(); EXPECT_NE(a.m, 0); } // UB!
        { FILL; E a = E(); EXPECT_EQ(a.m, 0); }
        { FILL; F a = F(); EXPECT_NE(a.m, 0); } // UB!
    }
    
    TEST(TestZero, StackListInitialization)
    {
        { FILL; A a{}; EXPECT_EQ(a.m, 0); }
        { FILL; B a{}; EXPECT_EQ(a.m, 0); }
        { FILL; C a{}; EXPECT_EQ(a.m, 0); }
        { FILL; D a{}; EXPECT_NE(a.m, 0); } // UB!
        { FILL; E a{}; EXPECT_EQ(a.m, 0); }
        { FILL; F a{}; EXPECT_NE(a.m, 0); } // UB!
    }
    
    TEST(TestZero, HeapDefaultInitialization)
    {
        { FILLH(b); A* a = new (b) A; EXPECT_EQ(a->m, 42); } // ~UB
        { FILLH(b); B* a = new (b) B; EXPECT_EQ(a->m, 42); } // ~UB
        { FILLH(b); C* a = new (b) C; EXPECT_EQ(a->m, 0);  }
        { FILLH(b); D* a = new (b) D; EXPECT_EQ(a->m, 42); } // ~UB
        { FILLH(b); E* a = new (b) E; EXPECT_EQ(a->m, 42); } // ~UB
        { FILLH(b); F* a = new (b) F; EXPECT_EQ(a->m, 42); } // ~UB
    }
    
    TEST(TestZero, HeapValueInitialization)
    {
        { FILLH(b); A* a = new (b) A(); EXPECT_EQ(a->m, 0);  }
        { FILLH(b); B* a = new (b) B(); EXPECT_EQ(a->m, 0);  }
        { FILLH(b); C* a = new (b) C(); EXPECT_EQ(a->m, 0);  }
        { FILLH(b); D* a = new (b) D(); EXPECT_EQ(a->m, 42); } // ~UB
        { FILLH(b); E* a = new (b) E(); EXPECT_EQ(a->m, 0);  }
        { FILLH(b); F* a = new (b) F(); EXPECT_EQ(a->m, 42); } // ~UB
    }
    
    TEST(TestZero, HeapListInitialization)
    {
        { FILLH(b); A* a = new (b) A{}; EXPECT_EQ(a->m, 0);  }
        { FILLH(b); B* a = new (b) B{}; EXPECT_EQ(a->m, 0);  }
        { FILLH(b); C* a = new (b) C{}; EXPECT_EQ(a->m, 0);  }
        { FILLH(b); D* a = new (b) D{}; EXPECT_EQ(a->m, 42); } // ~UB
        { FILLH(b); E* a = new (b) E{}; EXPECT_EQ(a->m, 0);  }
        { FILLH(b); F* a = new (b) F{}; EXPECT_EQ(a->m, 42); } // ~UB
    }
    
    int main(int argc, char **argv)
    {
        ::testing::InitGoogleTest(&argc, argv);
        return RUN_ALL_TESTS();
    }
    

    The places where UB! is mentioned are undefined behavior, and the actual behavior is likely to depend on many factors (a.m might be equal to 42, 0, or some other garbage). The places where ~UB is mentioned are also undefined behavior in theory, but in practice, due the use of a placement new, it's very unlikely than a->m will be equal to anything else than 42.

提交回复
热议问题