c++ 构造函数调用顺序
构造函数的执行次序如下:
1、调用基类构造函数,调用顺序按照他们的继承时声明的顺序。
2、调用内嵌成员对象的构造函数,调用顺序按照他们在类中声明的顺序
3、派生类的构造函数体中的内容
析构函数的调用顺序相反
先看一个拷贝构造函数的调用,Point拷贝构造函数调用了几次???Line拷贝构造函数又调用了几次???
#include <iostream> #include <cmath> using namespace std; class Point { public: Point(int xx=0,int yy=0) { X = xx; Y = yy;
cout<<"Point 原函数被调用"<<endl;
} Point(Point &p); int GetX() { return X; } int GetY() { return Y; } private: int X,Y; }; Point::Point(Point &p) { X = p.X; Y = p.Y; cout << "Point 拷贝构造函数被调用"<<endl; } class Line { public: Line(Point xp1,Point xp2); Line(Line &); double GetLen() { return len; } private: Point p1,p2; double len; }; Line::Line(Point xp1,Point xp2):p1(xp1),p2(xp2) { cout <<"Line 构造函数被调用!"<<endl; double x = double(p1.GetX()-p2.GetX()); double y = double(p1.GetY()-p2.GetY()); len = sqrt(x*x + y*y); } Line::Line(Line &L):p1(L.p1),p2(L.p2) { cout<<"Line拷贝构造函数被调用"<<endl; len = L.len; } int main() { Point myp1(1,2),myp2(3,4); Line line(myp1,myp2); Line line2(line); cout <<"The length of the line is:"; cout <<line.GetLen()<<endl; cout <<"The length of the line2 is:"; cout <<line2.GetLen()<<endl; return 0; }
运行结果:
程序分析:
1、主程序在执行时,首先声称两个Point类的对象,然后构造Line类的对象line,接着通过拷贝构造函数建立Line类的第二个对象line2,最后输出两点的距离。
2、由运行结果结果可知,Point类的拷贝构造函数被调用6次,而且都是在Line类的拷贝构造函数被调用1次
先看前四个Point类的拷贝构造函数:
1、程序在主函数中,执行line(myp1,myp2)时,会进行一个实参与形参的传递过程,在此过程中会调用Point类的拷贝构造函数,即相当于xp1=myp1 ,xp2=myp2;总计两次
2、然后在进行初始化内嵌对象时调用p1(xp1),p2(xp2);总计两次
然后再看后两个Point类的拷贝构造函数:
3、程序在主函数中,执行line2(line)时,依然会进行一个实参与形参的传递过程,然后进行初始化内嵌对象再调用Point类的构造函数,即相当于L.p1 = p1,L.p2= p2 ;总计两次
4、那么对于Line类也是如此.....
接下来我们再来去理清构造函数调用顺序
1、先看第一个实例,例子是最好的说明!
#include <iostream> using namespace std; class A1 { public: A1(int i) { cout <<"A1 " <<i <<endl; } }; class A2 { public: A2(int i) { cout <<"A2 " <<i <<endl; } }; class A3 { public: A3() { cout <<"A3 "<<endl; } }; class B:public A2,public A1,public A3 {
A3 a3;
A2 a2;
A1 a1;
public: B(int a,int b,int c,int d):A1(a),A2(b),a1(c),a2(d){} }; int main(int argc, char* argv[]) { B b(1,2,3,4); return 0; }
运行结果:
疑问:为什么会有以上结果?带着疑问去思考!
众所周知构造函数的执行次序如下:
调用基类构造函数,调用顺序按照他们的继承时声明的顺序。
调用内嵌成员对象的构造函数,调用顺序按照他们在类中声明的顺序。
这大概是强调第二次了!
程序分析:那么再来看以上的例子就很容易理解了。A2、A1、A3是B的积累,这便是调用顺序也是继承时声明的顺序。
按照上述的顺序,我们先要构造基类,然后才是子对象,最后是其本身的构造函数所以先要执行这三个类的构造函数。
在构造时按照他们在类中的顺序:
- 首先调用A2的构造函数
A2(int i) { cout <<"A2 " <<i <<endl; }
由于在默认参数列表:
B(int a,int b,int c,int d):A1(a),A2(b),a1(c),a2(d){}
中,将b的值传给A2的构造函数,b为2,所以打印出:A2 2
2.接下来要构造的是A1
显然在B的默认参数构造列表中将a的值传给A1,故打印出A1 1
3、A3在构造时没有传递参数,
会调用
A3() { cout <<"A3 "<<endl; }
这时基类的构造函数已经执行完毕,接着该处理内嵌成员对象的构造函数了
我们看到B类有三个对象:
A3 a3;
A2 a2;
A1 a1;
按照构造函数的调用顺序,我们序言按照类中声明的顺序来分别构造a3、a2、a1.在默认的参数列表中,先调用本身的构造函数,由于函数体为空,a3调用。用c来构造a1,用d来构造a2。
对于派生类调用总结一下:
我们必须明确的是当一个类继承与基类,并且自身还包含有其他类的成员对象的时候,构造函数调用顺序为:调用基类的构造函数--->调用成员对象的构造函数------>调用自身的构造函数。要牢记一点:构造函数的调用次数完全不受构造函数初始化列表的表达式中德次序影响,与基类的声明次数和成员对象在函数中德声明次序有关!!!
为大家充分理解,再看类似一个例子!
#include <iostream> using namespace std; class A { protected: char c; public: A(char ch) { c = ch; cout <<"c="<<c<<endl; cout <<"类A构造函数被调用"<<endl; } ~A() { cout<<"类A析构函数被调用"<<endl; } }; class B { protected: int i; public: B(int j) { i=j; cout <<"i = "<<i<<endl; cout <<"类B构造函数被调用"<<endl; } ~B() { cout <<"类B析构函数被调用"<<endl; } }; class C:public A,B { private: int k; public: C(char ch,int ii,int kk):A(ch),B(ii),k(kk) { cout <<"k="<<k<<endl; cout <<"类C构造函数被调用"<<endl; } ~C() { cout <<"类C析构函数被调用"<<endl; } }; int main(int argc, char* argv[]) { C A('h',10,15); return 0; }
运行结果:
如果说这个例子还不会,还不理解,那只能说你比较........你懂得!!!
以上讲述的这些例子只是派生类的构造函数调用顺序!!后续还有虚基类的构造函数!