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

在我正在阅读的书中( C ++ Without Fear ),它说如果你没有为一个类声明一个默认的构造函数,那么编译器会为你提供一个,它将“清除每个数据成员”。 我已经试验过了,我没有看到任何调零行为。 我也找不到在Google上提到的任何内容。 这只是一个特定编译器的错误或怪癖吗?

如果你没有定义一个构造函数,编译器会为你定义一个默认构造函数。

这个的实现

默认的构造函数是:

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

注意:
POD数据(int,float,pointer等)没有明确的构造函数,但默认的行为是什么都不做(C ++哲学的风格;除非我们明确地要求,否则我们不想付出代价) 。

如果没有定义析构函数/复制构造函数/赋值运算符,那么编译器将为您构build其中的一个(因此,类总是具有析构函数/复制构造函数/赋值运算符(除非您作弊并显式声明一个,但未定义它))。
默认的实现是:

析构函数:

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

复制构造函数:

  • 调用基类复制构造函数。
  • 按照声明的顺序调用每个成员variables的拷贝构造函数。

作业人员:

  • 调用基类赋值操作符
  • 以声明的顺序调用每个成员variables的赋值运算符。
  • 返回对此的引用。

注意复制POD Data的构造/赋值操作符只是复制数据(因此与RAW指针相关的浅拷贝问题)。

我认为值得指出的是,如果你不提供任何构造函数 ,默认的构造函数将只被编译器创build。 这意味着如果你只提供了一个带参数的构造函数,编译器将不会为你创build默认的无参数构造函数。

你书中提到的清零行为可能是特定编译器特有的。 我一直认为它可以改变,你应该明确地初始化任何数据成员。

  • 编译器是否自动生成一个默认的构造函数?
  • 隐式生成的默认构造函数是否执行零初始化?

如果你在法律上parsing了2003标准的语言,那么答案是肯定的 ,而不是 。 然而, 这不是一回事,因为与用户定义的默认构造函数不同, 隐式定义的默认构造函数在从头创build对象时并不总是使用 – 还有两种情况: 没有构造成员方式的值初始化

“没有build设”的情况实际上只是一个技术性,因为它在function上与调用简单的默认构造函数没有区别。 另一种情况更有意思:通过使用“()”(如显式调用没有参数的构造函数)来调用成员方式的值初始化, 并绕过技术上称为默认构造函数的内容 。 相反,它对每个数据成员recursion执行值初始化,而对于基本数据types,这最终会解决为零初始化

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

  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 

注意:如果用户声明的默认构造函数确实存在,那么成员方式的值初始化只是简单地调用它并停止。


这里有一个比较详细的细节标准说这个…

  • 如果你不声明一个构造函数,编译器会隐式地创build一个默认的构造函数[12.1-5]

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

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

  • 值初始化的净效果是隐式声明的默认构造函数永远不会被调用 。 相反,调用一个recursion的成员智能值初始化,最终将初始化任何原始成员,并在任何具有用户声明构造函数的成员上调用默认构造函数[8.5-5]

  • 值初始化甚至适用于原始types – 它们将被初始化为零。 [8.5-5]

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

所有这些对于大多数目的来说都是非常有意义的。 一个类的作者通常不能假定数据成员在一个隐含的初始化序列中被清零 – 所以任何自我pipe理类都应该定义它自己的构造函数,如果它有任何需要初始化的原始数据成员的话。

那么这是什么时候呢?

  • 通用代码可能会强制初始化未知types。 值初始化提供了一种方法来做到这一点。 请记住,如果用户提供了构造函数,则不会发生隐式的零初始化。

  • 默认情况下,std :: vector包含的数据被初始化。 这可以防止内存debugging器识别与未初始化的内存缓冲区相关的逻辑错误。

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

     new int[100](); 

这篇文章有更多关于标准版本之间差异的细节,同时也注意到标准在主要编译器中应用不同的情况。

C ++确实会生成一个默认的构造函数,但前提是你不提供你自己的构造函数。 该标准没有提到数据成员的清零。 默认情况下,当你第一次构build任何对象时,它们是未定义的。

这可能会令人困惑,因为大多数C ++基元types都有默认的“构造函数”,它们将它们初始化为零(int(),bool(),double(),long() ),但编译器不会调用他们可以像初始化对象成员一样初始化POD成员。

值得注意的是,STL 确实使用这些构造函数来默认构build容纳原始types的容器的内容。 你可以看看这个问题 ,了解更多关于STL容器中的东西如何被引入的细节。

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

 class Foo { public: int x; Foo() : x(1) {} }; class Bar { public: int y; Foo f; Foo *fp; }; int main() { Bar b1; ASSERT(b1.fx == 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(fx == 1); // We know nothing about what y is set to, or what fp is set to. } 

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

在C ++(和C)中,任何分配的数据的内容都不能保证。 在debuggingconfiguration中,某些平台会将其设置为一个已知的值(例如0xFEFEFEFE)来帮助识别错误,但这不应该被依赖。

只有对全局variables才进行清零。 所以如果你的对象是在全局范围内声明的,它的成员将被清零:

 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 } 

C ++不保证清零内存。 Java和C#做(以一种说话的方式)。

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

C ++生成一个默认的构造函数。 如果需要(在编译时确定,我相信),它也会生成一个默认的拷贝构造函数和一个默认的赋值构造函数。 我还没有听说任何有关保证内存归零的内容。

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

对于构造函数是非平凡的构造函数,以下是任何人都可以满足的条件:

1)这个类有一个虚拟的成员函数。 2)类成员子对象或基类有不平凡的构造函数。 3)一个类有虚拟inheritance层次。

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

  • 该类有一个参考字段
  • 或没有用户定义的默认构造函数的const字段
  • 或者是一个没有默认构造函数的字段,并且有一个被删除的默认构造函数

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