C++中各种初始化方式示例详解

2020-01-06 17:24:18丽君

color: #ff0000">前言

本文主要给大家介绍了关于C++初始化方式的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

C++小实验测试:下面程序中main函数里a.a和b.b的输出值是多少?


#include <iostream>

struct foo
{
 foo() = default;
 int a;
};

struct bar
{
 bar();
 int b;
};

bar::bar() = default;

int main()
{
 foo a{};
 bar b{};

 std::cout << a.a << 't' << b.b;
}

答案是a.a是0,b.b是不确定值(不论你是gcc编译器,还是clang编译器,或者是微软的msvc++编译器)。为什么会这样?这是因为C++中的初始化已经开始畸形发展了。

接下来,我要探索一下为什么会这样。在我们知道原因之前,先给出一些初始化的概念:默认初始化,值初始化,零初始化。


T global;    //T是我们的自定义类型,首先零初始化,然后默认初始化

void foo()
{
 T i;  //默认初始化
 T j{}; //值初始化(C++11)
 T k = T(); //值初始化
 T l = T{}; //值初始化(C++11)
 T m(); //函数声明

 new T; //默认初始化
 new T(); //值初始化
 new T{}; //值初始化(C++11)
}

struct A
{
 T t;
 A() : t() //t将值初始化
 {
 //构造函数
 }
};

struct B
{
 T t;
 B() : t{} //t将值初始化(C++11)
 {
 //构造函数
 }
};

struct C
{
 T t;
 C()  //t将默认初始化
 {
 //构造函数
 }
};

上面这些不同形式的初始化方式有点复杂,我会对这些C++11的初始化做一下简化:

默认初始化 :如果 T 是一个类,那么调用默认构造函数进行初始化;如果是一个数组,每个元素默认初始化,否则不进行初始化,其值未定义。至于 合成的 默认构造函数初始化数据成员的规则是:1.如果类数据成员存在类内初始值,则用该值初始化相应成员(c++11);2.否则,默认初始化数据成员。 值初始化 :如果 T 是一个类,那么类的对象进行默认初始化( 如果T类型的默认构造函数 不是 用户自定义的,默认初始化之前先进行零初始化 );如果是一个数组,每个元素值初始化,否则进行零初始化。 零初始化 :对于static或者thread_local变量将会在其他类型的初始化之前先初始化。如果T是算数、指针、枚举类型,将会初始化为0;如果是类类型,基类和数据成员会零初始化;如果是数组,数组元素也零初始化。

看一下上面的例子,如果T是int类型,那么global和那些T类型的使用值初始化形式的变量都会初始化为0(因为int是内置类型,不是类类型,也不是数组,将会零初始化,又因为int是算术类型,如果进行零初始化,则初始值为0)而其他的默认初始化都是未定义值。

回到开头的例子,现在我们已经有了搞明白这个例子所必要的基础知识。造成结果不同的根本原因是:foo和bar被它们不同位置的默认构造函数所影响。