不管是在c++,还是c#,或是java中,当人们一提到构造函数是,马上就回有人回答,是用来初始化成员变量的,没错,但是殊不知,后面却隐含了很多东西。
首先:构造函数用初始化式与在函数里面直接初始化有什么不同。
我先简单说一下初始化式,可能有人已经忘了什么是构造函数的初始化式。其实初始化式就是一个以冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟着一个放在圆括号里的初始化式。而初始化式只能在构造函数的定义中而不能再声明中指定。如:
class A{ public int a,b; public string c; A(const string &s):a(3),b(4),c(s){ } };
我们也可以由下面的方式进行成员变量的初始化:
class A{ public int a,b; public string c; A(const string &s){ a = 3;b = 4;c = s} };
那么上面两个有什么区别呢?
我们先说一下,构造函数的执行过程分为两个阶段:(1)初始化阶段;(2)普通计算阶段。
普通计算阶段是由构造函数的函数体内的语句来构成。
那么我们来分析一下第二种方式的初始化:
这个构造函数在给类A的成员变量赋值之前,第一步要进行初始化变量c。所以这个构造函数使用了内部隐式的string类的构造函数对c进行初始化。当我们执行到普通计算阶段时,又对c进行了赋值。
那么什么样的构造函数必须使用初始化式来对成员变量进行赋值呢?
我这里给出两种,一种是const,另一种是引用类型。
为什么呢?我们可以想一想const变量要求在声明的时候就进行初始化,而如果我们采用第二种方式的话,在构造函数的第二个阶段,又会对const变量赋一次值,而这又违反了const变量的性质。关于引用类型的探讨留给读者。
第二个问题:
初始化列表中的次序
我们一般可能会认为初始化列表的顺序就是初始化成员函数的顺序,其实不是这样。而应该是定义成员变量的顺序。
下面给一个例子:
class M{
int i;
int j;
public:
M(int val):j(val),i(j){}
};
这个例子看起来似乎是用val初始化j,然后用j再初始化i。
然而,i其实是被先初始化的,他用未被初始化的j来初始化。
第三个问题
先看下面的程序
class M{
public: string a;
M():a(10,'a'){}
};
void main()
{
char* c = new char[10];
M m;
strcpy(c,m.a.c_str());
cout<<c;
}
这个程序中输出了九个a
其实在初始化式中,如果有其他类类型的变量,可以直接使用那个类的构造函数来对这个M类中的对象初始化。例子中就是用string类的构造函数来初始化的。
至于为什么用c_str()函数,可以看下面这篇文章。
http://blog.csdn.net/gaotengguojianhong/article/details/7037942