类对象的构造顺序是这样的:ios
1.分配内存,调用构造函数时,隐式/显示的初始化各数据成员;ide
2.进入构造函数后在构造函数中执行通常赋值与计算。函数
使用初始化列表有两个缘由:spa
缘由1.必须这样作:.net
《C++ Primer》中提到在如下三种状况下须要使用初始化成员列表:code
状况1、须要初始化的数据成员是对象的状况(这里包含了继承状况下,经过显示调用父类的构造函数对父类数据成员进行初始化); 对象
状况2、须要初始化const修饰的类成员或初始化引用成员数据;blog
状况3、子类初始化父类的私有成员;继承
若是咱们有一个类成员,它自己是一个类或者是一个结构,并且这个成员它只有一个带参数的构造函数,而没有默认构造函数,这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,若是没有初始化列表,那么他将没法完成第一步,就会报错。内存
举个栗子:
#include <iostream> using namespace std; class Test { public: Test (int, int, int) { cout <<"Test" << endl; }; private: int x; int y; int z; }; class Mytest { public: Mytest():test(1,2,3) { //初始化 cout << "Mytest" << endl; }; private: Test test; //声明 }; int main() { Mytest test; return 0; }
① 若是没有mytest():test(1,2,3){}初始化列表就会报错:
由于Test有了显示的带参数的构造函数,那么他是没法依靠编译器生成无参构造函数的,因此没有三个int型数据,就没法建立Test的对象。Test类对象是MyTest的成员,想要初始化这个对象test,那就只能用成员初始化列表,没有其余办法将参数传递给Test类构造函数。
②初始化列表在构造函数执行前执行(这个能够看上面的结果,对同一个变量在初始化列表和构造函数中分别初始化,首先执行参数列表,后在函数体内赋值,后者会覆盖前者)。
#include <iostream> using namespace std; // const class Test { priate: const int a; //const成员声明 public: Test():a(10){} //初始化 }; // 引用 class Test { private: int &a; //声明 public: Test(int a):a(a){} //初始化 }
#include <iostream> using namespace std; class Test{ public: Test(){ cout << "111111111";}; Test (int x){ int_x = x;}; void show(){cout<< int_x << endl;} private: int int_x; }; class Mytest:public Test{ public: Mytest() :Test(110) { // 构造函数只能在初始化列表中被显示调用(若是不显示初始化,默认调用默认构造函数),不能在构造函数内部被显示调用 // 内部调用的是临时变量 //Test(110); }; }; int main() { Test *p = new Mytest(); p->show(); return 0; }
结果:若是在构造函数内部被显示调用输出结果是:-842150451(缘由是这里调用了无参构造函数);
若是在初始化列表中被显示调用输出结果是:110
缘由2.效率要求这样作:
类对象的构造顺序显示,进入构造函数体后,进行的是计算,是对成员变量的赋值操做,显然,赋值和初始化是不一样的,这样就体现出了效率差别,若是不用成员初始化类表,那么类对本身的类成员分别进行的是一次隐式的默认构造函数的调用,和一次赋值操做符的调用,若是是类对象,这样作效率就得不到保障。
注意:构造函数须要初始化的数据成员,不管是否显示的出如今构造函数的成员初始化列表中,都会在该处完成初始化,而且初始化的顺序和其在类中声明时的顺序是一致的,与列表的前后顺序无关,因此要特别注意,保证二者顺序一致才能真正保证其效率和准确性。
为了说明清楚,假设有这样一个类:
class foo { private: int a, b; };
①、foo(){}和foo(int i = 0){}都被认为是默认构造函数,由于后者是默认参数。二者不能同时出现。
②构造函数列表的初始化方式不是按照列表的的顺序,而是按照变量声明的顺序。好比foo里面,a在b以前,那么会先构造a再构造b。因此不管foo():a(b + 1), b(2){}仍是foo():b(2),a(b+1){}都不会让a获得指望的值。
③构造函数列表可以对const成员初始化。好比foo里面有一个int const c;则foo(int x) : c(x){}可让c值赋成x。
不过须要注意的是,c必须在每一个构造函数(若是有多个)都有值。
④在继承里面,只有初始化列表能够构造父类的private成员(经过显示调用父类的构造函数)。好比说:
class child : public foo{};
foo里面的构造函数是这样写的:
foo (int x) { a =x; }
而在child里面写child(int x){ foo(x); }是经过不了编译的。
只有把子类构造函数写做child(int x) : foo(x){}才能够。
原文连接:https://blog.csdn.net/sinat_20265495/article/details/53670644