变量初始化的方式

初始值

当对象在创建时获得了一个特定的值,我们说这个对象被初始化了。用于初始化变量的值可以是任意复杂的表达式。当一次定义了两个或多个变量时,对象的名字在定义后就马上可以使用了。例如我们可以这样使用:

    // 使用刚刚定义的price初始化discount
    double price = 109.99, discount = price * 0.16;

这里需要注意的是,初始化和赋值是两个完全不同的操作。初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,然后以一个新值代替。

列表初始化

C++11标准规定,无论是初始化对象还是某些时候给一个对象赋予新值都可以使用一组由花括号括起来的初始值。这种初始化形式被称为列表初始化。这样的初始化形式有一个重要特点:如果我们使用列表初始化时可能导致信息的丢失,则编译器不会通过,例如:

    long double ld = 3.1415926535;
    int a{ld}, b = {ld};        // 错误:需要收缩转换
    int c(ld), d = ld;          // 正确:但是值会被截断

默认初始化

如果定义变量没有指定一个初始值,则这个变量会被默认初始化,这样的变量被赋予“默认值”。这个默认值是什么由变量的类型和变量的位置决定。

如果内置类型的变量未被显式初始化,则它的值取决于它在哪里被定义。在任何函数体外部定义的变量被初始化为0。而定义于函数内部的内置类型变量是未初始化的。一个未被初始化的内置类型的变量的值是未定义的,如果试图拷贝或以其他形式访问此类值将引发错误。

每个类自己决定我们初始化这个类类型对象的方式。进一步说,我们是否能够不初始化地定义这个类的对象也是由类自己决定。如果我们能够不经初始化就定义对象,那么这个类自己决定对象的初始值将会是什么。

直接初始化和拷贝初始化

如果使用等号(=)初始化一个变量,实际上执行的是拷贝初始化;反之,如果不使用等号,则执行的是直接初始化。例如:

    string s1 = "hi";        // 拷贝初始化
    string s2("hi");         // 直接初始化
    string s3(10, 'c');      // 直接初始化

通常来说,类似于s3这类的初始化值有多个的情况,一般只能使用直接初始化,但是非要用拷贝初始化也有办法,显式地创建一个(临时)对象用于拷贝即可:

    string s4 = string(10, 'c')    // 拷贝初始化

值初始化

以vector为例,通常情况下,可以给vector对象只提供容纳元素的数量而略去初始值。此时库会创建一个值初始化的元素赋给容器中的所有元素。这个初值由vector对象中元素的类型决定。

如果vector中的元素是内置类型,例如int类型,则初值将是0。如果是一个类类型,例如string,则元素由其默认初始化:

    vector<int> ivec(10);    // 10个元素,每个都被初始化为0
    vector<string> svec(10); // 10个元素,每个都是一个空string对象

这种初始化方式有两个特殊限制:其一是一些类要求必须明确地提供初始值,如果vector对象中元素的类型不支持默认初始化,我们就必须提供初始化元素值。对于这种类型的对象,只提供一个元素数量而没有设定初始值无法完成初始化工作。

其二是如果只提供了元素的数量而没有设定初始值,只能使用直接初始化:

    vector<int> v1 = 10;     // 错误:必须使用直接初始化的形式指定向量大小
    vector<int> v2(10);      // 正确

究其深层原因,是因为接收一个容量参数的vector构造函数是explicit的,所以不能够使用“=”来隐式转换。

posted @ 2020-03-20 14:15  southernEast  阅读(1481)  评论(0编辑  收藏  举报