构造函数的成员初始化列表

为什么要初始化成员

对于类成员是基础数据类型,例如intchar这些,构造对象时,成员不会被初始化,值是随机的。下面代码可以验证下:

class A
{
public:
    A(){}

    void showMember() const
    {
        std::cout << "a:" << a << std::endl;
    }
private:
    int a;
};

int main()
{
    A a;
    a.showMember();
}

运行结果是会变化的,此时我的IDE已经提示我,未初始化成员,因为值不确定,会带来不确定结果,良好的习惯是初始化他们。

成员初始化的方法

  • 在构造函数内初始化

对于习惯C开发的我来说,顺其自然的想到就是在构造函数里面初始化这些成员。

A()
{
    a = 0;
}

A(int v)
{
    a = v;
}
  • 使用成员初始化列表

但是,假设有一个buffer类,分配指定大小的buffer使用,通常会有一个成员记录创建对象时buffer的大小,方便类中方法判断对此块内存的操作边界,一般是固定的,所以为了防止意外篡改,可以将此成员用const修饰。

class MyBuffer
{
public:
    MyBuffer(int size)      // <<===编译错误
    {
        buffer = new char[size];
        capacity = size;    // <<===编译错误
    }

    ~MyBuffer()
    {
        delete[] buffer;
    }
private:
    char *buffer;
    const int capacity;     // <<===编译错误
};

const修饰的变量是要在声明时就初始化的,否则后面通过赋值来修改它的值就必然违背这个关键字设计的初衷了。那上面提到的构造函数里面通过赋值的方式初始化const int capacity就行不通了。C++11标准引入了构造函数成员初始化列表,写法为:

ClassName(para1, para2, ...):menber1(para1),menber2(para2),...

具体到Mybuffer就是:

explicit MyBuffer(int size) : capacity(size)
{
    buffer = new char[size];
}

同样的,对于声明为引用类型的成员,它也是要求声明时就初始化好。初始化成员的方式还有一种写法就是直接在声明成员时。

class MyBuffer
{
private:
    char *buffer;
    const int capacity = 10;
    //const int capacity{10};或者这种写法
};

显然,这种方法有一个比较大的缺点,无法通过构造函数动态的决定这个成员的值,类定义好后,它永远是10,你改不了。
当然这两种方式在汇编层面来说毫无差异:汇编对比

使用初始化列表的良好习惯

  • 初始化列表的顺序保持和成员声明的顺序一致。

看下面的例子:

class A
{
    /*我的IDE已提示这个地方有问题
     *              |
     *              |
     *              V                  */
public:
    A() : n2(0), n1(n2 + 1)
    {
    }

    void print()
    {
        std::cout << "n1: " << n1 << ", n2: " << n2 << std::endl;
    }

private:
    int n1;
    int n2;
};

int main()
{
    A a;
    a.print();
    return 0;
}

运行结果:

n1: 32768, n2: 0

实际我们的预期是:A() : n2(0), n1(n2 + 1),n2 = 0; n1 = n2 + 1 = 0 + 1 = 1,为什么会这样?这个和初始化列表赋值的顺序有关,编译器生成的初始化动作是按照类成员声明的顺序来的。所以编译器会先初始化n1,n1 = n2 + 1, 而此时n2还未初始化,是随机值,这就导致n1也是随机值,随后n2 = 0。也就是说如果成员初始化有依赖关系时,要特别注意初始化顺序。
可以对比不同的初始化列表顺序,发现他们的汇编都是按照类成员声明属性来赋值的。

    explicit A(int p1, int p2) :a(p1),b(p2)                   
    {
    }

    explicit A(int p1, int p2) :b(p2),a(p1)
    {
    }

不同初始化列表顺序的对比

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章