【问题标题】:c++ implicit class member initialization for (POD struct, POD class and POD) membersc ++隐式类成员初始化(POD结构,POD类和POD)成员
【发布时间】:2014-12-15 20:10:18
【问题描述】:

我试图了解隐式类成员初始化对于成员 {POD 结构、POD 类和 POD} 的工作原理。在阅读了一些之后,我希望它们被初始化为它们的默认值,但这里的实际行为似乎有所不同 -

#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> >();
}

明显的运行结果不足以说明 POD 是初始化为默认值 0 还是仅包含噪声。但无论如何,这里有一些结果:

使用 gcc 4.6.3 #define SKIP_S1C1 在 ubuntu 上构建并运行它,我明白了

!:      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

并把它注释掉,我得到

:       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

在 VS2013 中,带有注释,

:       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

并且未注释,

!:      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

当涉及到 {POS 结构、POD 类和 POD} 成员的隐式初始化时,我真的很想了解我应该期待什么以及何时是 UB。任何帮助将不胜感激... :)

【问题讨论】:

  • 标量不是隐式值初始化的,用户定义的类是。

标签: c++ constructor


【解决方案1】:

构造函数很复杂,细节是技术性的,但这里是一个通用的总结*:

有三种初始化方式:

  • 零初始化 - 细节是技术性的,但实际上将所有位设置为零。这绕过了构造函数
  • 默认初始化 - 如果它有构造函数,则调用默认构造函数。否则,不会发生初始化。 从这些中读取的是您找到的 UB。
  • 值初始化 - 如果它有构造函数,则调用默认构造函数。否则,它的位都(有效地)设置为零。

在很多情况下都会调用它们:

  • 静态全局变量 - 零初始化,然后值初始化。 (很奇怪)
  • locals - 默认初始化。
  • new T; - 默认初始化
  • new T(); - 值初始化
  • 成员不在初始化列表中 - 默认初始化
  • 初始化列表中的成员 - 值初始化。

有关详细信息,请参阅 C++11 草案中的第 8.5 节和第 12.6 节。它们又长又无聊。

另请注意,C 的规则在技术上令人惊讶地不同,尽管效果在我看来是相同的。

*我的总结在技术上并不准确,但对于大多数真实代码来说已经足够准确了。例如,数组在技术上具有特殊的规则,但它们太直观以至于不值得一提。

**是的,它的“初始化”是“无初始化”,这使得其他关于“如果它被初始化”的段落在技术上模棱两可,但适用常识。它没有初始化。

【讨论】:

  • 啊,谢谢,这是一个完美的总结,回答了我所有的问题,包括我试图通过测试代码涵盖的其他一些问题,即使没有明确询问...... :)
  • 如果可以的话,只是一个小小的说明 - 结构和类构造函数之间没有区别,对吧?
  • 我承认只是略读代码,如果代码中有任何特定的测试仍然令人困惑,请告诉我,我会尝试找出那部分。
  • @Guest86:structclass 之间的唯一区别是struct 中的继承和成员默认为public。除了那个微小的细节,它们是 100% 相同的。大多数人将class 用于复杂的事物,将struct 用于“普通旧数据”,但没有真正好的理由。
猜你喜欢
  • 1970-01-01
  • 2015-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-07
  • 2013-06-27
  • 1970-01-01
相关资源
最近更新 更多