C++的变量初始化

C++中变量的初始化有不少种方式,如:默认初始化,值初始化,直接初始化,拷贝初始化,列表初始化。

 

一、默认初始化:默认初始化是指定义变量时没有指定初值时进行的初始化操做。函数

如:int a;这些变量被定义了而没有显式的赋予初值。spa

特别的,采用动态分配内存的方式(即采用new关键字)建立的变量,不加括号时(如int *p=new int;)也是默认初始化,加了括号(如int *p=new int())为值初始化。code

默认初始化变量的值与变量的类型与变量定义的位置有关系:对象

(1)对于内置类型变量(如int,double,bool等),若是定义在语句块外(即{}外),则变量被默认初始化为0;若是定义在语句块内(即{}内),变量将拥有未定义的值。内存

(2)对于类类型的变量(如string或其余自定义类型),无论定义于何处,都会执行默认构造函数。若是该类没有默认构造函数,则会引起错误。所以,建议为每一个类都定义一个默认构造函数(=default)。ci

 

二、值初始化:值初始化是指使用了初始化器(即便用了圆括号或花括号)但却没有提供初始值的状况。v8

例如:int *p = new int(); vector<string> vec(10);字符串

注意,若不采用动态分配内存的方式(即不采用new运算符),写成int a();是错误的值初始化方式,由于这种方式是声明了一个函数而不是进行值初始化。编译器

若是必定要进行值初始化,必须结合拷贝初始化使用,即写成int a=int();值初始化和默认初始化同样,对于内置类型初始化为0,对于类类型则调用其默认构造函数,若是没有默认构造函数,则不能进行初始化。string

 

三、直接初始化:直接初始化是指采用小括号的方式进行变量初始化(小括号里必定要有初始值,若是没提供初始值,那就是值初始化了!)。

例如:int a(12); vector<int> ivec(ivec2);string s("123456");等等。

直接初始化直接调用与实参匹配的构造函数。

 

四、拷贝初始化:拷贝初始化是指采用等号(=)进行初始化的方式,编译器把等号右侧的初始值拷贝到新建立的对象中去。

例如int a=12;string s=string("123456");等等。拷贝初始化看起来像是给变量赋值,其实是执行了初始化操做,与先定义再赋值本质不一样。

拷贝初始化首先使用指定构造函数建立一个临时对象,而后用拷贝构造函数将那个临时对象拷贝到正在建立的对象。

 

直接初始化与拷贝初始化:简而言之复制初始化使用“=”符号,而直接初始化将初始化式放在圆括号中。

(1)对于内置类型变量(如int,double,bool等),直接初始化与拷贝初始化差异能够忽略不计。

(2)对于类类型的变量(如string或其余自定义类型),直接初始化调用类的构造函数(调用参数类型最佳匹配的那个),拷贝初始化调用类的拷贝构造函数。

特别的,当对类类型变量进行初始化时,若是类的构造函数采用了explicit修饰并且须要隐式类型转换时,则只能经过直接初始化而不能经过拷贝初始化进行操做。

 

五、列表初始化:列表初始化是C++ 11 新引进的初始化方式,它采用一对花括号(即{})进行初始化操做。能用直接初始化和拷贝初始化的地方都能用列表初始化,并且列表初始化能对容器进行方便的初始化,所以在新的C++标准中,推荐使用列表初始化的方式进行初始化。列表初始化的应用场景有:int a{12};string s{"123"};vector<int> vec{1,2,3};

在某些状况下,初始化的真实含义依赖于传递初始值时用的是花括号仍是圆括号,例如,用一个整数来初始化vector<int>时,整数的含义多是vector对象的容量也多是元素的值。相似的,用两个整数来初使化vector<int>时,这两个整数可能一个是vector对象的容量,另外一个是元素的初值,也可能它们是容量为2的vector对象中两个元素的初值。经过使用花括号或圆括号能够区分上述这些含义:

vector<int>v1(10); //v1有10个元素,每一个的值都是0

vector<int> v2{10}; //v2有1个元素,该元素的值时10

vector<int> v3(10,1); //v3有10个元素,每一个的值都是1

vector<int> v4{10,1}; //v4有两个元素,值分别是10,1

若是用圆括号,能够说提供的值是用来构造vector对象的。例如,v1的初始值说明了vector对象的容量;v3的两个初始值则分别说明了vector对象的容量和元素的初值。

若是用的是花括号,能够表述成咱们想列表初始化该vector对象。也就是说,初始化过程会尽量地把花括号内的值当成是元素初始值的列表来处理,只有在没法执行列表初始化时才会考虑其余初始化方式。在上例中,给v2和v4提供的初始值都做为元素的值,因此它们都会执行列表初始化,vector对象v2包含一个元素而vector对象v4包含两个元素。

另外一方面,若是初始化时使用了花括号的形式可是提供的值又不能用来列表初始化,就要考虑用这样的值来构造vector对象了。例如,要想列表初始化一个含有string对象的vector对象,应该提供能赋给string对象的初值。此时不难区分究竟是要列表初始化vector对象的元素仍是用给定的容量值来构造vector对象:

vector<string> v5{"hi}; //列表初始化,v5有一个元素

vector<string> v6("hi"); //错误,不能使用字符串字面值构造vector对象

vector<string> v6(10,"hi");//正确,10个string类型,每一个初始值为“hi”。

以上两条能够看出,vector<string> v6(1,"hi");就能达到你的目的,为何还要画蛇添足,再提供一个专门的构造呢

vector<string> v7(10); //v7有10个默认初始化的元素

vector<string> v8{10,"hi"}; //v8有10个值为”hi“的元素

 

六、使用new动态分配的默认初始化和值初始化

默认状况下,动态分配对象是默认初始化的,这意味着内置类型或组合类型的对象的值时未定义的,而类类型对象将默认构造函数进行初始化:

string *ps=new string;  //初始化为空string

int *pi =new int;   //pi指向一个未初始化的int

咱们可使用直接初始化方式来初始化动态分配的对象。咱们可使用传统的构造方式,在新标准下,也可使用列表初始化(使用花括号):

int *pi=new int(1024);  //pi指向的对象的值为1024

string *ps=new string(10,'9');   //*派生为“9999999999”

//vector有10个元素,值依次从0-9

vector<int> *pv=new vector<int>{0,1,2,3,4,5,6,7,8,9};

也能够对动态分配的对象进行值初始化,只需在类型名以后跟一对空括号便可:

string *ps1=new string ;//默认初始化为空string

string *ps=new string(); //值初始化为空string

int *pi1=new int;   //默认初始化;*pi1的值未定义

int *pi2=new int(); //值初始化为0;*pi2的值为0

对于定义了本身的构造函数的类类型来讲,要求值初始化是没有意义的;无论采用什么方式,对象都会经过默认构造函数来初始化。但对于内置类型,两种形式的差异就大了;值初始化的内置类型对象有着良好定义的值,而默认初始化的对象的值则是未定义的。相似的,对于类中那些依赖于编译器合成的默认构造函数的内置类型成员,若是它们未在类内初始化,那么它们的值也是未定义的。

相关文章
相关标签/搜索