代码之家  ›  专栏  ›  技术社区  ›  Skilldrick

C++中是否存在隐式默认构造函数?

  •  59
  • Skilldrick  · 技术社区  · 16 年前

    在我正在读的书中( C++ Without Fear )它表示,如果不为类声明默认的构造函数,编译器将为您提供一个构造函数,它“将每个数据成员归零”。我已经试验过了,我没有看到任何归零的行为。我在谷歌上也找不到任何提到这个的地方。这只是一个错误还是一个特定编译器的怪癖?

    11 回复  |  直到 9 年前
        1
  •  61
  •   Mansuro    13 年前

    如果不定义构造函数,编译器将为您定义默认的构造函数。

    这一点的实施

    默认构造函数为:

    • 默认构造基类(如果基类没有默认构造函数,这是编译失败)
    • 默认情况下,按照声明的顺序构造每个成员变量。(如果成员没有默认构造函数,则这是编译失败)。

    注:
    POD数据(INT、浮点、指针等)没有明确的构造函数,但默认的操作是什么都不做(在C++哲学的指导下,我们不想花钱,除非我们明确地要求它)。

    如果没有定义析构函数/复制构造函数/赋值运算符,编译器将为您生成其中一个(因此类始终具有析构函数/复制构造函数/赋值运算符(除非您欺骗并显式声明了一个,但没有定义它))。
    默认实现是:

    Destructor:

    • 如果定义了用户定义的析构函数,请执行提供的代码。
    • 以声明的相反顺序调用每个成员的析构函数
    • 调用基类的析构函数。

    复制构造函数:

    • 调用基类复制构造函数。
    • 按照声明的顺序为每个成员变量调用复制构造函数。

    分配运算符:

    • 调用基类赋值运算符
    • 按声明顺序调用每个成员变量的赋值运算符。
    • 返回对此的引用。

    注:POD数据的复制构造/分配运算符只是复制数据(因此与原始指针相关的浅复制问题)。

        2
  •  35
  •   Bill the Lizard    16 年前

    我认为值得指出的是,只有当您提供 没有任何构造函数 . 这意味着如果只提供一个接受参数的构造函数,编译器将 为您创建默认的无参数构造函数。

    你的书中提到的归零行为可能是特定编译器特有的。我一直假设它可以变化,您应该显式初始化任何数据成员。

        3
  •  33
  •   Community Mohan Dere    8 年前
    • 编译器是否自动生成默认构造函数?
    • 隐式生成的默认构造函数是否执行零 初始化?

    如果您合法地分析2003标准的语言,那么答案是 . 然而, 这不是整个故事 因为与用户定义的默认构造函数不同, 不总是使用隐式定义的默认构造函数 从头开始创建对象时——还有两个场景: 无结构 成员级值初始化 .

    “无结构”案例实际上只是一个技术性的问题,因为它在功能上与调用 琐碎的 默认构造函数。另一种情况更有趣:通过使用“()”[就像显式调用没有参数的构造函数]和 它绕过了技术上所说的 这个 默认构造函数 . 相反,它递归地对每个数据成员执行值初始化,对于基元数据类型,这最终解析为 零初始化 .

    所以实际上, 编译器提供两个不同的隐式定义的默认构造函数 . 其中之一 对原始成员数据执行零初始化,而另一个则不执行。下面是一些如何调用每种类型的构造函数的示例:

        MyClass a; // default-construction or no construction
        MyClass b = MyClass(); // member-wise value-initialization
    

        new MyClass; // default-construction or no construction
        new MyClass(); // member-wise value-initialization
    

    注:如果 用户声明 默认构造函数 存在,然后成员级值初始化只调用该值并停止。


    以下是标准中关于这一点的详细说明…

    • 如果不声明构造函数,编译器将隐式创建默认构造函数[12.1-5]

    • 默认构造函数 初始化基元类型[12.1-7]

      MyClass() {} // implicitly defined constructor
      
    • 如果使用“()”初始化对象,则不会直接调用默认构造函数。相反,它引发了一系列被称为 值初始化 [85-7]

    • 值初始化的净效果是 从未调用隐式声明的默认构造函数 . 相反,将调用递归成员级值初始化,该初始化最终将 零初始化任何基元成员 并对具有用户声明的构造函数的任何成员调用默认构造函数[8.5-5]

    • 值初始化甚至应用于基元类型——它们将被零初始化。[85-5]

      a = int(); // equivalent to int a=0;
      

    所有这些在大多数情况下都是没有意义的。类的编写器一般不能假定在隐式初始化序列期间数据成员将被清零——因此,如果任何自管理类具有任何需要初始化的基元数据成员,那么它应该定义自己的构造函数。

    那么这什么时候重要呢?

    • 在某些情况下,泛型代码可能希望强制初始化未知类型。值初始化提供了一种实现这一点的方法。请记住,如果用户提供了构造函数,则不会发生隐式零初始化。

    • 默认情况下,std::vector包含的数据是值初始化的。这可以防止内存调试程序识别与未初始化的内存缓冲区相关联的逻辑错误。

      vector::resize( size_type sz, T c=T() ); // default c is "value-initialized"
      
    • 可以使用值初始化语法对基元类型或“普通旧数据”(pod)类型结构的整个数组进行零初始化。

      new int[100]();
      

    This post 更详细地介绍了标准版本之间的差异,并指出了标准在主要编译器中应用不同的情况。

        4
  •  20
  •   Community Mohan Dere    8 年前

    C++只生成一个默认构造函数,但仅当不提供自己的构造函数时才生成默认构造函数。标准中没有提到将数据成员归零的问题。默认情况下,当您第一次构造任何对象时,它们是未定义的。

    这可能是令人困惑的,因为大多数C++原语类型都有默认的“构造函数”,将它们归零(in-()、布尔()、双()、长(), 等。 但是编译器不会像对对象成员那样调用它们来初始化pod成员。

    值得注意的是 使用这些构造函数默认构造包含基元类型的容器的内容。你可以看看 this question 有关如何初始化STL容器中的内容的更多详细信息。

        5
  •  11
  •   Salman A    16 年前

    为类创建的默认构造函数不会初始化内置类型,但它将对所有用户定义的成员调用默认构造函数:

    class Foo
    {
    public:
         int x;
         Foo() : x(1) {}
    };
    
    class Bar
    {
    public:
         int y;
         Foo f;
         Foo *fp;
    };
    
    int main()
    {
    
        Bar b1; 
        ASSERT(b1.f.x == 1); 
        // We know nothing about what b1.y is set to, or what b1.fp is set to.
    
        // The class members' initialization parallels normal stack initialization.
        int y;  
        Foo f; 
        Foo *fp; 
        ASSERT(f.x == 1);
        // We know nothing about what y is set to, or what fp is set to.
    
    }
    
        6
  •  5
  •   Andrew Grant    16 年前

    如果用户创建的构造函数和析构函数不存在,编译器将生成默认的构造函数和析构函数。这些不会修改任何数据成员的状态。

    在C++(和C)中,任何分配的数据的内容都不能保证。在调试配置中,一些平台会将此值设置为已知值(例如,0xfefefefe),以帮助识别错误,但不应依赖此值。

        7
  •  4
  •   codelogic    16 年前

    只有全局变量才出现零输出。因此,如果您的对象在全局范围内声明,则其成员将为零:

    class Blah
    {
    public:
        int x;
        int y;
    };
    
    Blah global;
    
    int main(int argc, char **argv) {
        Blah local;
        cout<<global.x<<endl;  // will be 0
        cout<<local.x<<endl;   // will be random
    }
    
        8
  •  4
  •   Jason Cohen    16 年前

    C++做的 保证内存归零。Java和C语言(以某种方式说)。

    有些编译器可能会,但不依赖于此。

        9
  •  1
  •   Indeed is Trash    16 年前

    C++生成一个默认构造函数。如果需要(在编译时确定),它还将生成一个默认的复制构造函数和一个默认的赋值构造函数。不过,我没有听说任何关于零内存的保证。

        10
  •  1
  •   rahul    12 年前

    默认情况下,编译器不会生成默认构造函数,除非实现不需要默认构造函数。 所以,基本上,构造函数必须是 非平凡构造函数 .

    对于非平凡的构造器,以下是任何一个构造器都可以满足的条件:

    1)类具有虚拟成员函数。 2)类成员子对象或基类具有非平凡的构造函数。 3)类具有虚拟继承层次结构。

        11
  •  1
  •   Nice Books    9 年前

    在C++ 11中,编译器生成的默认构造函数被标记为删除,如果:

    • 类具有引用字段
    • 或没有用户定义的默认构造函数的常量字段
    • 或没有默认初始值设定项的字段,具有已删除的默认构造函数

    http://en.cppreference.com/w/cpp/language/default_constructor