默认构造函数会初始化内置类型吗?

206

编译器创建的默认构造函数会初始化内置类型吗?

7个回答

207

编译器隐式定义的类默认构造函数不会初始化内置类型的成员变量。

然而,你需要记住,在某些情况下,类的实例可以通过其他方式进行初始化。既不是默认构造函数,也不是任何构造函数。

例如,有一种普遍的错误观念认为,对于类 C,语法 C() 总是调用默认构造函数。实际上,语法 C() 执行所谓的值初始化,而仅当它是用户声明的时才会调用默认构造函数。(这是在C++03中。在C++98中,仅适用于非POD类)。如果类没有用户声明的构造函数,则 C() 将不会调用编译器提供的默认构造函数,而是执行一种不涉及 C 构造函数的特殊初始化。相反,它将直接值初始化类的每个成员。对于内置类型,它将导致零初始化。

例如,如果你的类没有用户声明的构造函数

class C { 
public:
  int x;
};

那么编译器将会自动提供一个构造函数。编译器提供的构造函数不会进行任何操作,也就是它不会初始化 C::x

C c; // Compiler-provided default constructor is used
// Here `c.x` contains garbage

尽管如此,以下初始化将会x进行零初始化,因为它们使用了显式的()初始化程序。

C c = C(); // Does not use default constructor for `C()` part
           // Uses value-initialization feature instead
assert(c.x == 0);

C *pc = new C(); // Does not use default constructor for `C()` part
                 // Uses value-initialization feature instead
assert(pc->x == 0);

()初始化器的行为在C++98和C++03之间在某些方面有所不同,但在这种情况下没有区别。对于上述类C,它将是相同的:()初始化器执行C::x的零初始化。

另一个在不涉及构造函数的情况下执行初始化的示例,当然是聚合初始化。

C c = {}; // Does not use any `C` constructors at all. Same as C c{}; in C++11.
assert(c.x == 0);

C d{}; // C++11 style aggregate initialization.
assert(d.x == 0);

4
根据https://dev59.com/Xm865IYBdhLWcg3wNLw7#3931589的说法,Visual Studio C++编译器存在一个错误,C c = C();可能并不总是起作用。 - Anton Daneyko
22
在C++11中:C c{}会将x初始化为0吗? - towi
17
如果你执行C() = default;,它会如何工作?这仍然会对new C();执行值初始化并对new C;执行默认初始化吗? - Mark Ingram
4
有点晚了,但是没关系:如果你使用C() = default;,那么对于new C();你会得到值初始化,对于new C;你会得到默认初始化。参考:https://dev59.com/C1gQ5IYBdhLWcg3w6IEH#42049188 - Chris Nolet
3
这个答案很好,但只涉及到 C 是 POD 的情况。如果这个答案能提到当 C 不是POD或非标准布局时情况会如何变化,那就更好了。 - Konrad Rudolph
显示剩余5条评论

24

我不太确定你的意思,但是:

struct A { int x; };

int a; // a is initialized to 0
A b;   // b.x is initialized to 0

int main() {
    int c;         // c is not initialized
    int d = int(); // d is initialized to 0

    A e;           // e.x is not initialized
    A f = A();     // f.x is initialized to 0
}
在我说“未初始化”的每种情况下 - 你可能会发现你的编译器给出了一个一致的值,但是标准并不要求它。
关于内置类型如何“实际上”具有默认构造函数,很多手续费被抛出来,包括我的,但事实上,默认初始化和值初始化是标准中定义的术语,个人每次都需要查阅。只有类在标准中被定义为具有隐式默认构造函数。

21

从实际目的来看 - 不行。


然而,对于符合C++标准的实现,答案取决于对象是否为POD以及如何初始化它。 根据C++标准:

MyNonPodClass instance1;//built in members will not be initialized
MyPodClass instance2;//built in members will be not be initialized
MyPodClass* instance3 = new MyPodClass;//built in members will not be initialized
MyPodClass* instance3 = new MyPodClass() ;//built in members will be zero initialized

然而,在实际应用中,这并不被很好地支持,因此不要使用它。


标准相关部分为第8.5.5节和第8.5.7节。


全局变量怎么样,它们不是总是被初始化为零吗? - fredoverflow
1
对于除第一个之外的所有内容,都没有调用默认构造函数。实际上,它们的默认构造函数都是相同的(它们不会初始化任何东西)-毕竟它们都是同一个类。在第四个中,编译器只是值初始化POD,并且不调用默认构造函数。 - Johannes Schaub - litb
1
@FredOverflow,所有命名空间范围和局部或类静态对象都会被零初始化,与它们的类型无关(它们可能是最复杂的类 - 仍然会被零初始化)。 - Johannes Schaub - litb
1
内置成员将不会被初始化。这是什么意思? - SWdV

3
根据标准,如果您没有在初始化程序列表中显式初始化,则不会发生这种情况。

3
在默认构造函数中,您无法指定任何内容由编译器创建 - Gorpik
2
@Gorpik -- 理解了...但是当我说显式初始化时,我的意思是必须显式提供默认构造函数。 - mukeshkumar
@hype:我知道,但OP指定他在谈论计算机创建的默认构造函数,而不是你自己提供的构造函数。 - Gorpik

1

正如之前的发言者所说 - 不,它们没有被初始化。

这实际上是一个非常奇怪的错误源,因为现代操作系统倾向于用零填充新分配的内存区域。如果你期望这样做,第一次可能会成功。然而,随着你的应用程序不断运行,deletenew对象,你迟早会遇到这样一种情况:你期望得到零,但实际上却有一个非零的残留物来自先前的对象。

那么,为什么会这样呢?难道所有new的数据都是新分配的吗?是的,但并不总是来自操作系统。操作系统倾向于使用更大的内存块(例如每次4MB),因此所有微小的单词-三个字节-这里分配和释放都在用户空间处理,因此不会被清零。

PS. 我写了“倾向于”,也就是说,你甚至不能指望第一次就成功...


-5

从技术上讲,它确实会初始化它们——通过使用它们的默认构造函数,这个构造函数仅仅为它们分配内存。

如果你想知道的是它们是否被设置为像int的0这样合理的值,那么答案是“不”。


6
构造函数不分配内存。构造函数在内存分配之后执行。如果我错了,请纠正我。 - ZoomIn

-8
不会。默认构造函数分配内存并调用任何父类的无参构造函数。

7
任何非POD成员的无参构造函数。 - Steve Jessop
构造函数是否分配内存,还是编译器为实例“分配”内存,然后调用构造函数? - visitor
7
这个回答相当错误... 1/ 构造函数不会分配任何内存,它只是对其进行初始化。2/ 这个问题涉及内置类,而这个回答却是关于父类的...为什么这个错的离谱的回答还获得了8个赞? - Matthieu M.
1
分配内存?这是从哪里来的? - AnT stands with Russia
1
我觉得很有趣,这个帖子有9个赞和5个踩,而最高评分的答案只有5个赞和0个踩。 - Johannes Schaub - litb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接