C++初始化列表

作者: 三分钟热度的陈恤猿 | 来源:发表于2018-08-05 10:37 被阅读32次

引言

       用c++的人都知道,c++的构造函数具有初始化列表,初始化列表有什么作用?什么情况下必须使用初始化列表?结合网上的相关博客,这里给出自己的一些理解。

构造函数的两个阶段

首先给出一个结论:初始化操作直接初始化成员,赋值操作则先初始化再赋值。
       构造函数的执行分为两个阶段:初始化阶段和计算阶段。
       1. 初始化阶段:在类中,所有的类成员都需要进行初始化。初始化阶段可以显式地对成员变量进行初始化。
       2. 计算阶段:计算阶段主要处理赋值操作以及一些其他的语句,计算阶段一定在初始化阶段完成后实现。
       初始化列表处于初始化阶段,其作用是在计算阶段之前,指定类成员变量的初始化方式。

       以下面的代码为例:
       Base类中有一个成员变量a,分别拥有默认构造函数、拷贝构造函数以及赋值运算符。

class Base
{
public:
    Base()
    {
        cout<<"Constructor for Base"<<endl;
    }
    
    Base(const Base& t)
    {
        cout<<"Copy Constructor for Base"<<endl;
        a=t.a;
    }
  
    Base& operator=(const Base& t)
    {
        cout<<"Assignment for Base"<<endl;
        a=t.a;
        return *this;
    }

private:
    int a;
}

       接下来,我们构造一个Test类来进行测试,其有个类型为Base的成员 变量b。

  • 不使用初始化列表

class Test
{
public:
    Test(Base& t)
    {
        b=t;
    }
private:
    Base b;
};

       调用代码

Base b;
cout<<"Test is running!"<<endl;
Test t(b);

       输出结果如下:

Constructor for Base
Test is running!
Constructor for Base
Assignment for Base

       输出结果表明,在构造Test类时,由于其构造函数没有初始化列表(意味着没有进行显性的变量初始化),在初始化阶段,编译器首先用Base的默认构造函数初始化Test的成员变量b,接着在计算阶段,执行赋值操作,将入参b赋值给了t.b。
       构造Test的过程中,调用了一次默认构造函数和一次赋值操作

       接下来我们改写Test的构造函数来测试初始化列表的作用。

  • 使用初始化列表

class Test
{
public:
    Test(Base& t):b(t){}
private:
    Base b;
};

       输出结果如下:

Constructor for Base
Test is running!
Copy Constructor for Base

       这一次由于使用了初始化列表,在初始化阶段我们显性地利用拷贝构造函数进行了成员变量的初始化,在计算阶段,不需要进行其他操作。
       构造Test的过程中,仅仅使用了一次拷贝构造函数,少调用了一次默认构造函数,节省了构造的效率。

为什么使用初始化列表

       经过上述测试我们不难发现,使用初始化列表减少了一次默认构造函数的调用。对于内置类型,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型,尤其是对于数据密集型的类来说,是非常高效的。因此,建议尽量使用初始化列表的方式。

哪些东西必须放在初始化列表中?

       由于在构造函数体内进行成员变量初始化需要调用默认构造函数并执行赋值操作,因此对于没有默认构造函数或者不能进行赋值操作的变量,利用这种方式显然是错误的!

  • 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
  • 引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
  • 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。

初始化列表的成员初始化顺序

       C++初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。

补充

       一个小小的试验。

       试验代码如下

class Base
{
public:
    Base()
    {
        cout<<"Constructor for Base"<<endl;
    }
    
    Base(const Base& t)
    {
        cout<<"Copy Constructor for Base"<<endl;
        a=t.a;
    }
    
    Base& operator=(const Base& t)
    {
        cout<<"Assignment for Base"<<endl;
        a=t.a;
        return *this;
    }
    
    ~Base()
    {
        cout<<"Destructor for Base"<<endl;
    }
    
private:
    int a;
};

class Test
{
public:
    Test(Base& t)
    {
        b=t;
    }
    
    ~Test()
    {
        cout<<"Destructor for Test"<<endl;
    }
private:
    Base b;
};

int main()
{
    Base b;
    cout<<"Test is running!"<<endl;
    {
        Test t(b);
    }
    cout<<"running!"<<endl;
    
    
    return 0;
}

       试验结果

Constructor for Base
Test is running!
Constructor for Base
Assignment for Base
Destructor for Test
Destructor for Base
running!
Destructor for Base

更新一下:为何在main函数中直接些MyClass a = XXX调用的是拷贝构造函数,因为在那里没有额外的声明,定义和声明在一起,所以调用拷贝构造函数;而在类中,已经声明过了,所以写这种方式,会先调用构造函数,再调用operator=操作符。

相关文章

  • C++初始化列表

    引言 用c++的人都知道,c++的构造函数具有初始化列表,初始化列表有什么作用?什么情况下必须使用初始化列表...

  • [C++之旅] 11 初始化列表

    [C++之旅] 11 初始化列表 初始化列表的特性 初始化列表先于构造函数执行 初始化列表只能用于构造函数 初始化...

  • C++的初始化列表和列表初始化

    C++的初始化列表和列表初始化 初始化列表 初始化列表是声明在构造函数中来实现的,相当于初始化,而不是复制操作 初...

  • C++初始化列表

    本文主要说明成员初始化列表的注意事项。 I、上帝视角看初始化列表 构造函数可以有两种构造形式,一是在构造函数体内对...

  • c++ 初始化列表

    初始化列表可以改变private里const 的值

  • C++之初始化列表

    问题 类中是否可以定义 const 成员? 编译结果: 代表类中可以定义const成员变量, 但是面临的问题是: ...

  • C++初始化列表 多态

    初始化列表 特点 一种便捷的初始化成员变量的方式 只能在构造函数中 初始化列表与默认参数配合使用 如果函数的声明和...

  • 成员初始化列表

    C++为类中提供类成员的初始化列表 类对象的构造顺序是这样的:1.分配内存,调用构造函数时,隐式/显示的初始化各数...

  • 001 列表初始化

    列表初始化 C++ 语言定义了初始化的好几种不同形式,这也是初始化问题复杂性的一个体现。例如,要想定义一个名为 u...

  • C++中的初始化列表

    类成员的初始化 首先来看一段代码: 请问上述代码可以执行么?答案是不可以,因为在Test类初始化时必须明确的初始化...

网友评论

    本文标题:C++初始化列表

    本文链接:https://www.haomeiwen.com/subject/fqovvftx.html