初始化列表

  • 分别从初始化列表的作用优点&为什么高效使用要求三方面介绍。

    1、初始化列表的作用:主要用于对于成员变量的初始化,使用方式是在构造函数后面使用一个冒号,再进行成员变量的赋值。例如,如下类中成员变量的赋值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Myclass{
    public:
    MyClass(int num1,int num1):i(num1),j(num2) // 使用初始化列表进行初始化
    {

    }
    ~MyClass()
    {

    }
    public:
    int i;
    int j;

    }

    注:在使用初始化列表进行初始化时需要按照类中定义变量的顺序进行初始化,否则可能会导致一些值没有初始化。

    1
    2
    3
    4
    MyClass(int num1):j(num1),i(j) // 编译器会首先初始化i,但由于j此时没有初始化,导致i值未定义
    {

    }

    结果如下:

    image-20220403162036138

    2、优点&原因

    初始化类的成员有两种方式:一种是使用初始化列表,另外一种是在构造函数体进行赋值操作。对于内置类型,如intfloat等通过初始化列表和构造函数初始化两者性能差别不大。但是对于类类型最好使用初始化列表,主要原因是少了一次调用默认的构造函数

    1
    2
    3
    4
    5
    class Test2
    {
    Test1 test1 ;
    Test2(Test1 &t1):test1(t1){}
    }

    输出结果如下:

    img

    原因:

    构造函数的两个执行阶段:初始化阶段计算阶段

    初始化阶段:所有类类型都会在初始化阶段初始化(调用构造函数)

    计算阶段:采用赋值构造函数,进行赋值(赋值构造函数)。

    如果不使用初始化列表的构造过程如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    Class Test1
    {
    Test1() // 无参构造函数
    {
    cout << "Construct Test1" << endl ;
    }

    Test1(const Test1& t1) // 拷贝构造函数
    {
    cout << "Copy constructor for Test1" << endl ;
    this->a = t1.a ;
    }

    Test1& operator = (const Test1& t1) // 赋值运算符
    {
    cout << "assignment for Test1" << endl ;
    this->a = t1.a ;
    return *this;
    }

    int a ;
    };

    Class Test2
    {
    Test1 test1 ;
    Test2(Test1 &t1)
    {
    test1 = t1 ;
    }
    };
    int main()
    {
    Test1 t1 ;
    Test2 t2(t1) ;
    return 0;
    }

    对于t2先调用构造函数(初始化),然后进行赋值构造函数(计算阶段)

    img

    3、使用要求

    除了性能问题之外,有一些特定场合必须使用初始化列表。

    • 常量成员:常量只能初始化不能赋值,因此必须放在初始化列表中。
    • 引用类型:引用必须在定义时初始化,并不能重新赋值,因此也需要放在初始化列表中。
    • 没有默认构造函数的类类型:初始化列表无需默认构造函数,直接调用拷贝构造函数初始化。