面向对象程序设计第三节-初始化列表(2020-01-27)

初始化列表 (Initializer List)

我们先定义一个Student类:

class Student {
private:
    string name;
public:
    Student(string s);
};

Student类具有一个数据成员name,还有一个构造函数。
我们再定义这个构造函数,有如下两种方式:

1.初始化列表(Initializer List)

Student::Student(string s) : name(s) {}
  1. 赋值(Assignment)
Student::Student(string s) { name = s;}

这两种方式虽然最终的效果一样,但是在实现过程中存在差异,简单地说,第二种方法虽然合法但是草率,数据成员name将在构造函数体之前执行默认初始化,再进行赋值,而第一种方法直接进行初始化。所以,虽然这两种方法的效果是一样的,但是,有些时候必须使用初始化列表:
当成员是const或者是引用时,必须将其初始化,这里借用一下《C++ Primer》中的例子来说明:

class Constref {
public:
    Constref(int ii);
private:
    int i;
    const int ci;
    int &ri;
};

构造函数如下:

Constref::Constref(int ii)
{   //赋值
    i = ii;
    ci = ii;
    ri = i;
}

我们在vs上看一下编译是否能通过,但是这段代码写完就发出警示了,如下:


面向对象程序设计第三节-初始化列表(2020-01-27)_第1张图片
warning

所以,如果成员中如果是cons或者是引用的话,还是使用初始化列表,如下:

Constref::Constref(int ii) :i(ii), ci(ii), ri(i) {}

建议: 使用构造函数初始值

在很多类中,初始化和赋值的区别事关底层效率问题:前者直接初始化数据成员,后者则先初始化再赋值。
除了效率问题外更重要的是,一些数据成员必须被初始化,建议读者养成使用构造函数初始值的习惯,这样能避免某些意想不到的编译错误,特别是遇到有的类含有需要构造函数初始值的成员时。

(From 《C++ Primer》)

还有一个问题需要注意的是成员初始化的顺序,看下面一个例子:

#include 
#include 
#include 

using namespace std;

class A {
private:
    int i;
    int j;
public:
    A(int val): j(val), i(j) {  }
    void f();
};

void A::f()
{
    cout << i << " " << j << endl;
}
int main()
{
    A a(10);
    a.f();
    while (1);
    return 0;
}

上面这个例子中,构造函数是先初始化 j 再初始化 i 的,这与声明的顺序不一致,可能凭感觉来说这并没有什么问题,但是,我们运行一下:


面向对象程序设计第三节-初始化列表(2020-01-27)_第2张图片
result

很明显,i 的初始化产生问题了,从形势上来看,仿佛是先用val初始化了j,再用 j 初始化 i ,实际上,成员的初始化顺序与它们在类定义中的出现顺序一致,即 i 先被初始化,因此这个初始值的效果是试图用未定义的值 j 来初始化 i ,这当然会出现一些无法预料的结果,所以,最好令构造函数初始值的初始顺序与成员声明的顺序一致,并且尽量避免使用某些成员初始化其他成员

你可能感兴趣的:(面向对象程序设计第三节-初始化列表(2020-01-27))