c++构造函数(初始化式)被忽略的东西

不管是在c++,还是c#,或是java中,当人们一提到构造函数是,马上就回有人回答,是用来初始化成员变量的,没错,但是殊不知,后面却隐含了很多东西。

首先:构造函数用初始化式与在函数里面直接初始化有什么不同。

我先简单说一下初始化式,可能有人已经忘了什么是构造函数的初始化式。其实初始化式就是一个以冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟着一个放在圆括号里的初始化式。而初始化式只能在构造函数的定义中而不能再声明中指定。如:

 

  1. class A{
  2.   public int a,b;
  3.  public string c;
  4.   A(const string &s):a(3),b(4),c(s){ }
  5. };

我们也可以由下面的方式进行成员变量的初始化:

  1. class A{
  2.   public int a,b;
  3.   public string c;
  4.   A(const string &s){ a = 3;b = 4;c = s}
  5. };

那么上面两个有什么区别呢?
我们先说一下,构造函数的执行过程分为两个阶段:(1)初始化阶段;(2)普通计算阶段。
普通计算阶段是由构造函数的函数体内的语句来构成。
那么我们来分析一下第二种方式的初始化:
这个构造函数在给类A的成员变量赋值之前,第一步要进行初始化变量c。所以这个构造函数使用了内部隐式的string类的构造函数对c进行初始化。当我们执行到普通计算阶段时,又对c进行了赋值。
那么什么样的构造函数必须使用初始化式来对成员变量进行赋值呢?
我这里给出两种,一种是const,另一种是引用类型。
为什么呢?我们可以想一想const变量要求在声明的时候就进行初始化,而如果我们采用第二种方式的话,在构造函数的第二个阶段,又会对const变量赋一次值,而这又违反了const变量的性质。关于引用类型的探讨留给读者。

第二个问题:
初始化列表中的次序
我们一般可能会认为初始化列表的顺序就是初始化成员函数的顺序,其实不是这样。而应该是定义成员变量的顺序。

下面给一个例子:

 

  1. class M{
  2.   int i;
  3.   int j;
  4.   public:
  5.   M(int val):j(val),i(j){}
  6. };

这个例子看起来似乎是用val初始化j,然后用j再初始化i。
然而,i其实是被先初始化的,他用未被初始化的j来初始化。
第三个问题
先看下面的程序

 

 

  1. class M{
  2. public: string a;
  3.  M():a(10,’a’){}
  4. };
  5. void main()
  6. {
  7.     char* c = new char[10];
  8.     M m;
  9.     strcpy(c,m.a.c_str());
  10.     cout<<c;
  11. }

这个程序中输出了九个a
其实在初始化式中,如果有其他类类型的变量,可以直接使用那个类的构造函数来对这个M类中的对象初始化。例子中就是用string类的构造函数来初始化的。
至于为什么用c_str()函数,可以看下面这篇文章。
http://blog.csdn.net/gaotengguojianhong/article/details/7037942

Posted in: c++

发表评论

电子邮件地址不会被公开。 必填项已用*标注