简体   繁体   中英

c++ implicit class member initialization for (POD struct, POD class and POD) members

I'm trying to understand how implicit class member initialization works for member {POD structs, POD classes and POD}s. After reading around a little I expected them to be initialized to their default values but the actual behavior seems to differ 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> >();
}

Obvious run results aren't enough to say whether the POD were initialized to their default value 0 or just contain noise. But here are some results, anyway:

Building and running it 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 out, 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 it commented,

:       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 uncommented,

!:      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 should I expect and when it's UB when it comes to implicit initialization of {POS struct, POD classes and POD} members. Any help would be greatly appreciated... :)

Constructors are complicated, and the details are technical, but here's a generic summary*:

There are three ways to initialize:

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

And they're called in many situations:

  • static globals - Zero Initialized, and then Value Initialized. (quite wierd)
  • locals - Default initialized.
  • new T; - Default Initialize
  • new T(); - Value Initialize
  • member not in init list - Default Initialize
  • member in init list - Value Initialize.

For more details refer to sections §8.5 and §12.6 in the C++11 draft. They're long and boring.

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

*My summary is not technically accurate, but is accurate enough for most real code. For instance, arrays have special rules technically, but they're so intuitive that they're not worth mentioning.

**Yes, it's "Initialization" is "No Initialization", which makes other paragraphs about "if it was initialized" ambiguous technically, but apply common sense. It's not initialized.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM