这里先给出结论,在贴出代码与执行结果~
一个派生类构造函数的执行顺序如下:
第一步执行:虚拟基类的构造函数(多个虚拟基类则按照继承的顺序执行构造函数)。
第二步执行:基类的构造函数(多个普通基类也按照继承的顺序执行构造函数)。
第三步执行:类类型的成员对象的构造函数(按照初始化顺序)。
第四部执行:派生类自己的构造函数。
如果一个派生类不仅继承于一个基类,而且还有这个基类的成员对象,那么会进行两次构造函数的执行(一个用于初始化派生类中基类部分的内部成员,另一个是初始化派生类的基类类型成员变量的内部成员),详细看派生类Son2的执行结果。
你
下面声明了A,B,C,D,Object1,Object2留个基类以及Son1,Son2,Son3,Son4,Son5(Son5是一个错误的例子,编译不能通过)
每个基类都有两个构造函数,默认的构造函数不接受参数,输出的Default+字符串
带参数的输出的自己类的特有信息。
(为了方便观看,我在后面会再贴一下结论)
参考Son1:可以验证上述派生类构造函数的执行顺序;
参考Son2:可以验证构造函数是严格照上面所说的顺序执行,与初始化的顺序无关。同时,如果不显示的执行基类构造函数的初始化,就会按照顺序调用默认的构造函数。
参考Son3:可以说明继承下执行的构造函数与类类型的成员变量的构造函数是占用两个不同的内存地址空间,二者不会相互影响。
参考Son4:可以说明,无论是否显示的初始化类类型的成员变量,都会按照成员变量在类中的声明顺序执行构造函数。
参考Son5:这个解决了我之前的疑问,如果在派生类的构造函数中初始化类类型的成员对象会怎么样,发现这样是不可取的,因为在类的声明中是不可以实际分配内存的,但是可以声明。
(关于Son5的理解需要进一步阐明:这里可能涉及到了C++内存分配,拷贝赋值的原理,如果不是很懂的话这里我们暂且按我说的方式去理解。)
classA
{
public:
A(intnum){Show();}
A(){cout<<"Defaultaaa"<<endl;}
voidShow(){cout<<"aaa"<<endl;}
};
classB
{
public:
B(intnum){Show();}
B(){cout<<"Defaultbbb"<<endl;}
voidShow(){cout<<"bbb"<<endl;}
};
classC
{
public:
C(intnum){Show();}
C(){cout<<"Defaultccc"<<endl;}
voidShow(){cout<<"ccc"<<endl;}
};
classD
{
public:
D(intnum){Show();}
D(){cout<<"Defaultddd"<<endl;}
voidShow(){cout<<"ddd"<<endl;}
};
classObject1
{
public:
Object1(intnum){Show();}
Object1(){cout<<"DefaultObject1"<<endl;}
voidShow(){cout<<"Object1"<<endl;}
};
classObject2
{
public:
Object2(intnum){Show();}
Object2(){cout<<"DefaultObject2"<<endl;}
voidShow(){cout<<"Object2"<<endl;}
};
classSon1:publicA,virtualpublicB,publicC,virtualpublicD
{
public:
Son1():A(1),B(1),C(1),D(1),ob1(1),ob2(1){
cout<<"son1"<<endl;
}
Object1ob1;
Object2ob2;
};
classSon2:publicA,virtualpublicB,publicC,virtualpublicD
{
public:
Son2():C(1),A(1),ob1(1),ob2(1){ //结果仍然是先执行A的构造函数,其次是C的,证明与初始
cout<<"son2"<<endl; //化的顺序无关
}
Object1ob1;
Object2ob2;
};
classSon3:publicA,virtualpublicB,publicC,virtualpublicD,virtualpublicObject1,publicObject2
{
public:
Son3():ob1(1),ob2(1){
cout<<"son3"<<endl;
}
Object1ob1;
Object2ob2;
};
classSon4:publicA,virtualpublicB,publicC,virtualpublicD,virtualpublicObject1,publicObject2
{
public:
Son4():ob2(1){ //注意,如果Object1没有默认构造函数,这里将无法编译通过
cout<<"son4"<<endl;
}
Object1ob1;
Object2ob2;
};
//class Son5:publicA,virtual public B,publicC,virtual public D
//{
//public:
// Son5():A(1),B(1),C(1),D(1){
// cout<<"son5"<<endl;
// ob2=ob1; //这里编译不通过,没有与这些操作数匹配的”=”运算符(二者类型不同,需要重新定义‘=’)
// ob1(1);
// ob2(2); //这里编译不通过,在没有适当的operate()的情况下调用类类型对象
// }
// Object1 ob1(1); // 这里编译不通过,因为类的成员声明不需要分配内存,这样写就相当于执行
//构造函数并分配内存了
// Object2 ob2;
//};
int_tmain(intargc, _TCHAR* argv[])
{
cout<<"------------SON1------------"<<endl;
Son1son1;
cout<<"------------SON2------------"<<endl;
Son2son2;
cout<<"------------SON3------------"<<endl;
Son3son3;
cout<<"------------SON4------------"<<endl;
Son4 son4;
Object1obj;
//son4.ob1(1); //这句话是错误的编译不通过,在没有适当的operate()的情况下调用类类型对象
son4.ob1=obj;
system("pause"); //这句只是为了让cmd停留显示,以免闪退(VS控制台程序需要)
return 0;
}
以上代码是在VS2012ConsoleApplication控制台下编译测试的,结果如下:
再贴一遍:
参考Son1:可以验证一开始介绍的派生类构造函数的执行顺序;
参考Son2:可以验证构造函数是严格照上面所说的顺序执行,与初始化的顺序无关(尽管表面上C比A初始化的要早)。同时,如果不显示的执行基类构造函数的初始化,就会按照顺序调用默认的构造函数。
参考Son3:可以说明继承下执行的构造函数与类类型的成员变量的构造函数是占用两个不同的内存地址空间,二者不会相互影响。
参考Son4:可以说明,无论是否显示的初始化类类型的成员变量,都会按照成员变量在类中的声明顺序执行构造函数。
参考Son5:这个解决了我之前的疑问,如果在派生类的构造函数中初始化类类型的成员对象会怎么样,发现这样是不可取的,因为在类的声明中是不可以实际分配内存的,但是可以声明。