C++ 默认初始化规则

什么是值初始化

对内置类型来说:值初始化就是设为0;对类类型来说,值初始化是调用默认构造函数。默认构造函数会对内置类型也进行值初始化(若不存在初始化列表或者类内初始值)。

使用值初始化的一些场景:

  • 数组初始化

    当我们显式为数组提供初始值时,当提供的初始值数量小于定义的数组大小时,剩余的数组元素会按照值初始化的方式进行,若数组元素为内置类型则为0,若为类类型,则必须具有默认构造函数,以供值初始化进行。

  • 局部静态变量

    当不使用初始值去显式初始化时,该静态变量会以值初始化的方式进行。

  • 对于像vector这种顺序容器可以用T(num)显式的要求值初始化的类型来说,也是按照值初始化的方式进行。 值初始化要求类应该具有默认构造函数。如果没有默认构造函数,需要显示的指定一个元素初始值。

例子:

 class A
 {
 private:
 public:
     int a;
     //int b;
     //A(int a, int b) : a(a), b(b) {}
     explicit A(int a) : a(a) {}
 };
 class B
 {
 private:
 public:
     int a;
 };
 ​
 int main(){
     int a[10]={1,2,3};//其余值初始化
     static int a;//值初始化
     std::vector<A> aa(5);//严重错误,A不存在默认构造函数 不可以值初始化。
     //不论下面的表达式定义在哪里,都会执行值初始化。
     std::vector<B> bb(5);//对每一个元素执行值初始化,bb[0].a=0;
     std::vector<A> aa(5,A(1)); //若不加explicit 可以写为std::vector<A> aa(5, 1);
     //std::vector<A> aa(5, A(1, 2));
     for (const auto &i  : aa)
     {
         std::cout << i.a << std::endl; // 1 1 1 1 1
     }
 }

什么情况下,不能依赖编译器为我们生成的合成的默认构造函数

  • 首先明确,只有在类未定义任何构造函数的情况下,编译器才会为我生成合成的默认构造函数

    所以,一旦在类中,显式定义了构造函数,编译器都不再会为我们生成默认构造函数

  • 合成的默认构造函数对内置类型不友好

    一旦类被定义在了函数体内,那些没有类内初始值的内置类型都不会被初始化。

  • 当类内包含有 “不包含默认构造函数的类类型”时,编译器将无法为我们合成默认构造函数

    此种情况要想使用默认构造函数,我们必须人为显式定义自己的默认构造函数。

内置类型默认初始化

  • 内置类型位于函数体外 (即全局变量

    此时执行值初始化,初始化为0.

  • 内置类型位于函数体内 (局部变量)

    此时不执行初始化,任何操作变量的行为都是未定义的。

  • 数组类型的默认初始化: 规则与上面相同。

  • 动态内存分配

    若未加()此时不执行初始化,任何操作变量的行为都是未定义的,加了()执行值初始化

类类型默认初始化

类类型如果没有提供初始化参数,会自动调用默认构造函数。

  • 默认构造函数对内置类型的处理:(优先级从高到低)

    • 在初始值列表内的,按照初始值列表赋值。

    • (C++11) 存在类内初始值的,初始化为对应的值。

    • 对于既不存在类内初始值,也不存在初始值列表里的,初始化与上述内置类型初始化相同:

      • 即如果类定义在函数体之外,初始化为0;

      • 类存在数组、容器中,要求值初始化的,对内置类型进行值初始化(如上述的几种值初始化场景)

      • 类定义在函数体之内,不进行初始化。

      • 类由动态内存申请,

        • 若加了(),则进行值初始化(0),

        • 若没有加(),则不做初始化,是未定义的

  • 对类类型的处理:(优先级从高到低)

    • 使用初始值列表进行初始化

    • 按照类内初始值进行初始化

    • 调用类的默认构造函数 (若不存在 会报错),默认构造函数会继续按照上述规则去运作。

动态内存分配

如上所述,默认情况下(类似于int *a =new int),动态内存分配的对象按上述规则进行默认初始化(不加括号 不做初始化,类调用默认构造函数)。

也可以使用直接初始化、列表初始化、值初始化来进行,例子如下:

//默认
 int *a =new int;//此时a为未定义
 //直接初始化
 int *pi=new int(1024);//初始化为1024
 string *str=new string(9,'c');//直接初始化
 //列表初始化
 vector<int> *a=new vector<int>{1,2};
 ​
 //值初始化
 int *a=new int();//此时int会被值初始化为0
 //对于定义了自己的合理的默认构造函数的对象来说,值初始化和默认初始化没有任何区别,但对于内置类型来讲,区别很大。值初始化会被会初始化为0,默认初始化是未定义。
 //此外,对于依赖编译器合成的默认构造函数的,例如:未自己定义默认构造函数,但是含有内置类型且内置类型没有类内初始值,此时加()会将其值初始化。

动态数组(动态数组不支持没有默认构造函数的类)

动态数组只支持三种初始化操作:

  • 默认初始化 其每个元素的初始化规则规则与上面所说相同。

 int *a=new int[10];
 Sale_data *s=new Sale_data[10];
  • 值初始化

  • int *a =new int[10]()
    
  • 列表初始化(C++11),若给定数目小于设置数目,则剩下元素执行值初始化,否则new表达式失败,抛出异常。

  • string *s=new string[10]{"a","b"}
    

    特殊的例子

    • cosnt和引用

      这两种类型都必须显示初始化,否则会报错。

    • 字符数组

      字符数组,可以使用字符串进行初始化。但是不可以使用字符串进行赋值。

      原理:“XXXXX”所进行的操作是在内存分配一定大小的空间并返回一个指向字符串头的const char *指针使用const char *指针对char数组进行初始化是符合C++和C的语法规则的。而字符数组在定义完成后,再使用数组名时会变成一个常量指针,指向数组的第一个元素。其内容不可更改,所以不能被赋值。(类似于const和引用 初始化之后不能被更改)

    函数的声明

    函数可以声明许多次,但只能定义一次(内联函数除外)。函数内不能定义函数(即函数不能嵌套定义),但可以在函数内声明函数(一般无用);

    普通构造函数的使用

    • 类在全局创建

      • 对内置类型

        • 初始值列表

        • 类内初始化

        • 值初始化

      • 对类类型

        • 初始值列表

        • 类内初始化

        • 调用默认构造函数

    • 类在函数内创建,唯一不同:对内置类型不执行值初始化。

    • 类由动态内存申请,与类在全局创建相同。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值