首先,我们来普及一个尽人皆知的简单事实:如果一个变量在定义的时候未被初始化,那么它的值会根据具体的存储位置或被处理成0,或被处理成随机值。
因此,在学习编程的初期,教导者一般都会告诉我们要谨慎对待初始变量,而对于类来说,最保险的做法是在每一个构造函数中,对每一个成员数据进行恰如其分的初始化。
这么简单的事情,还需要注意什么吗?
让我们来看一个最简单的例子:
class node1 { public: node1(int xx, int yy); private: int x; int y; };
此例中,如果x的值未被对象构造函数初始化,其数值是不确定的“随机值”,因此我们也许会在构造函数中这么写:
node1::node1(int xx, int yy) :x(xx) { y = yy; }
其中,x 以构造函数特有的语法(初始化列表)的形式被初始化,而 y 则在构造函数中被赋值。
以上两种做法显然都可以使得在类对象调用构造函数之后,保证 x 和 y 的值都是确定的数,但我们要牢记的是,初始化(initialization)和普通的赋值(assignment)语句是有区别的,他们的区别是:
class node2 { public: node2(int aa, int rr); private: int const a; int &r; };
上述代码中的a和r,是两个必须初始化的量(语法要求),在他们的在构造函数中必须被如下处理:
node2::node2(int aa, int rr) :a(aa), r(rr) // 使用初始化列表来赋初值 { }
另外要注意一点,类中的初始化次序是固定的:首先初始化基类(如果有的话),然后按照类中声明的次序初始化派生类的每一个成员。这个次序不受构造函数的初始化列表的次序的影响。换句话说,以上代码即便改成这样:
node2::node2(int aa, int rr)
:r(rr), a(aa) // 依然会先初始化a,再初始化r
{ }
初始化的次序依然是a、r,因为类声明中就是先a再r。这在某些需要注意成员初始化次序(比如先得有数组大小,再能定义数组)的场合特别值得留意。