C ++ class member initialization for class members (POD, POD and POD)

I am trying to understand how implicit initialization of class members works for {POD structs, POD classes and POD} s members. After reading a little bit, I expected them to be initialized to their default values, but the actual behavior seems to be different here -

#include <iostream>

struct S1
{
    void* a;
    int b;
};

struct S2
{
    S2() { std::cout << "!"; }
    void* a;
    int b;
};

struct S3
{
    S3() : a(), b() { std::cout << "!"; }
    void* a;
    int b;
};

class C1
{
public:
    void* a;
    int b;
};

class C2
{
public:
    C2() { std::cout << "!"; }
    void* a;
    int b;
};

class C3
{
public:
    C3() : a(), b() { std::cout << "!"; }
    void* a;
    int b;
};


template <typename T>
class FOO1
{
public:
    T s;
    int a;
};

template <typename T>
class FOO2
{
public:
    FOO2() {}
    T s;
    int a;
};

template <typename T>
class FOO3
{
public:
    FOO3() : s(), a() {}
    T s;
    int a;
};

//#define SKIP_S1C1

template <typename T>
void moo()
{
#ifndef SKIP_S1C1
    T* f = new T();
    T foo = *f;
    std::cout << ":\ts = (" << foo.s.a << ", " << foo.s.b << ")\ta = " << foo.a << std::endl;
    delete f;
#else
    T foo;
    std::cout << ":\ts = (" << foo.s.a << ", " << foo.s.b << ")\ta = " << foo.a << std::endl;
#endif
}


int main()
{
#ifndef SKIP_S1C1
    moo<FOO1<S1> >();
#endif
    moo<FOO1<S2> >();
    moo<FOO1<S3> >();
#ifndef SKIP_S1C1
    moo<FOO1<C1> >();
#endif
    moo<FOO1<C2> >();
    moo<FOO1<C3> >();

std::cout << std::endl;

#ifndef SKIP_S1C1
    moo<FOO2<S1> >();
#endif
    moo<FOO2<S2> >();
    moo<FOO2<S3> >();
#ifndef SKIP_S1C1
    moo<FOO2<C1> >();
#endif
    moo<FOO2<C2> >();
    moo<FOO2<C3> >();

std::cout << std::endl;

#ifndef SKIP_S1C1
    moo<FOO3<S1> >();
#endif
    moo<FOO3<S2> >();
    moo<FOO3<S3> >();
#ifndef SKIP_S1C1
    moo<FOO3<C1> >();
#endif
    moo<FOO3<C2> >();
    moo<FOO3<C3> >();
}

      

The obvious execution results are not enough to tell if the PODs were initialized to their default value of 0 or just contained noise. But here are some results:

Build and run on ubuntu with gcc 4.6.3 #define SKIP_S1C1

uncommented, I get

!:      s = (0x7ffffe557770, 4196620)   a = 1
!:      s = (0, 0)      a = 1
!:      s = (0, 0)      a = 1
!:      s = (0, 0)      a = 1

!:      s = (0x1, 6299744)      a = 6299744
!:      s = (0, 0)      a = 6299744
!:      s = (0, 0)      a = 6299744
!:      s = (0, 0)      a = 6299744

!:      s = (0x1, 6299744)      a = 0
!:      s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0

      

and with it commented, I get

:       s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0
:       s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0

:       s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0
:       s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0

:       s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0
:       s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0
!:      s = (0, 0)      a = 0

      

and with VS2013, with a comment,

:       s = (00000000, 0)       a = 0
!:      s = (CDCDCDCD, -842150451)      a = -842150451
!:      s = (00000000, 0)       a = -842150451
:       s = (00000000, 0)       a = 0
!:      s = (00000000, 0)       a = 0
!:      s = (00000000, 0)       a = 0

:       s = (CDCDCDCD, -842150451)      a = -842150451
!:      s = (CDCDCDCD, -842150451)      a = -842150451
!:      s = (00000000, 0)       a = -842150451
:       s = (00000000, 0)       a = 0
!:      s = (00000000, 0)       a = 0
!:      s = (00000000, 0)       a = 0

:       s = (00000000, 0)       a = 0
!:      s = (CDCDCDCD, -842150451)      a = 0
!:      s = (00000000, 0)       a = 0
:       s = (00000000, 0)       a = 0
!:      s = (00000000, 0)       a = 0
!:      s = (00000000, 0)       a = 0

      

and no comment,

!:      s = (CCCCCCCC, -858993460)      a = -858993460
!:      s = (00000000, 0)       a = -858993460
!:      s = (00000000, 0)       a = 0
!:      s = (00000000, 0)       a = 0

!:      s = (CCCCCCCC, -858993460)      a = -858993460
!:      s = (00000000, 0)       a = -858993460
!:      s = (00000000, 0)       a = 0
!:      s = (00000000, 0)       a = 0

!:      s = (CCCCCCCC, -858993460)      a = 0
!:      s = (00000000, 0)       a = 0
!:      s = (00000000, 0)       a = 0
!:      s = (00000000, 0)       a = 0

      

I would really like to understand what I should expect and when is it UB when it comes to implicit initialization of {POS struct, POD and POD} members. Any help would be greatly appreciated ... :)

+3


source to share


1 answer


The constructors are tricky, the details are technical, but here's a general summary *:

There are three ways to initialize:

  • Zero Initialize - The details are technical, but effectively sets all bits to zero. This bypasses the constructors
  • Default Initialize - If it has a constructor, the default constructor is called. Otherwise, initialization fails. Reading from them is the UB you found.
  • Initialize value - If it has a constructor, the default constructor is called. Otherwise, its bits are all (effectively) set to zero.

And they are called in many situations:

  • static global variables - null initialized and then value initialized. (rather strange)
  • locals - initialized by default.
  • new T;

    - Initialization by default
  • new T();

    - Initialize value
  • member not in initialization list - Default Initialize
  • member in the initialization list is the Initialize value.


See sections ยง8.5 and ยง12.6 in the C ++ 11 draft for details. They are long and boring.

Also note that the rules for C are technically surprisingly different, although the effects seem the same to me.

* My summary is not technically accurate, but accurate enough for most real-world codes. For example, arrays have special rules technically, but they are so intuitive that they are not worth mentioning.

** Yes, this is "Initialization" is "No initialization", which makes the other paragraphs about "if they were initialized" technically ambiguous, but apply common sense. It is not initialized.

+4


source







All Articles