C++为什么需要成员初始化列表
转自:http://blog.csdn.net/ws1347913745/article/details/8682331
将构造函数分为两个阶段的执行过程:初始化阶段和构造函数函数体阶段。
既然称它为成员初始化列表,那么该阶段在初始化阶段完成。
那么类成员变量不外乎类类型和非类类型。而对于非类类型而言,此时
无论是在初始化表中还是函数体内完成赋值效果是一致的,即并未初始化。
表现的有点不同的是类类型:
test1:
1 #include<iostream> 2 using namespace std; 3 class B 4 { 5 public: 6 B() 7 { 8 cout<<"B()"<<endl; 9 } 10 B(int i) 11 { 12 cout<<"B(int i)"<<endl; 13 } 14 }; 15 class A 16 { 17 public: 18 A() 19 { 20 cout<<"A()"<<endl; 21 } 22 private: 23 B b; 24 }; 25 int main() 26 { 27 A a; 28 return 0; 29 }
B类的默认构造函数被调用来初始化 b 对象了,也就是说对于类类型对象将调用它的默认构造函数完成初始化。
如果在成员初始化列表中显示地用B类接收int 形参的构造函数会怎么样:
test2:
1 #include<iostream> 2 using namespace std; 3 class B 4 { 5 public: 6 B() 7 { 8 cout<<"B()"<<endl; 9 } 10 B(int i) 11 { 12 cout<<"B(int i)"<<endl; 13 } 14 }; 15 class A 16 { 17 public: 18 A() 19 { 20 cout<<"A()"<<endl; 21 } 22 private: 23 B b; 24 }; 25 int main() 26 { 27 A a; 28 return 0; 29 }
B成员的对象直接调用了带int形参的构造函数,利用成员初始化列表我们完成了类成员的初始化工作。
那如果将他放入函数体中呢:
test3:
1 #include<iostream> 2 using namespace std; 3 class B 4 { 5 public: 6 B() 7 { 8 cout<<"B()"<<endl; 9 } 10 B(int i) 11 { 12 cout<<"B(int i)"<<endl; 13 } 14 }; 15 class A 16 { 17 public: 18 A() 19 { 20 cout<<"A()"<<endl; 21 } 22 private: 23 B b; 24 }; 25 int main() 26 { 27 A a; 28 return 0; 29 }
可以看见,它同样可以达到想要的结果,可是此时我们期望的是利用带参数的构造函数初始化B类对象,并不期望多此一举的
先调用默认构造。而之所以这样就是因为我们并未将初始化在函数体之前完成,而是将它带入了函数体。
所以,对于类类型对象的成员变量初始化过程:
1 编译器发现有类对象的定义时,首先找到了成员初始化列表,如果未找到相应的初始化定义,
则调用类的默认构造函数来完成初始化。找到则采用列表中的定义。
2 如果用户希望采取非默认构造初始化对象,而有将其放在函数体中,则此时一定要为该类提供默认的
构造函数,否则编译报错。
3 可以看出,如果你显示定义了带参构造,那么总是为类提供一个默认的构造函数是好的习惯,可以帮你避免许多可能麻烦。
4 对于某些必须在定义的同时初始化的变量,成员初始化列表就是非用不可的了,如const或则是引用类型,否则编译报错。
5 注意上面所说的是类类型成员对象的表现,而非引用或指针的表现。
特殊的成员:static成员它是在什么时候初始化的呢?答案是在第一次用到该成员时,因为static成员在内存中只有一份的拷贝,
它在整个程序阶段只被初始化一次。可以往A中加入static变量定义,发现实例化A对象后static变量未被初始化。
总之。如何利用好成员的初始化列表还是很重要的。