C-组合类中构造函数的调用顺序
以线段(Line)类为例子,梳理C++组合类中构造函数的调用顺序。
Line类源程序
//Line类计算线段长度
#include<iostream>
#include<cmath>
using namespace std;
class Point { //Point类定义
public:
Point(int xx = 0, int yy = 0) { //Point类构造函数
x = xx;
y = yy;
cout << "Calling constructor of Point" << endl;
}
Point(Point& p); //Point类复制构造函数
int getX() { return x; }
int getY() { return y; }
private:
int x, y;
};
Point::Point(Point& p) { //Point类复制构造函数的实现
x = p.x;
y = p.y;
cout << "Calling the copy constructor of Point" << endl;
}
//类的组合
class Line { //Line类定义
public: //外部接口
Line(Point xp1, Point xp2); //Line类构造函数
Line(Line& l); //Line类复制构造函数
double getLen() { return len; }
private: //私有数据成员
Point p1, p2; //Point类的对象p1,p2
double len;
};
//组合类的构造函数
Line::Line(Point xp1, Point xp2) :p1(xp1), p2(xp2) {
cout << "Calling constructor of Line" << endl;
double x = static_cast<double>(p1.getX() - p2.getX());
double y = static_cast<double>(p1.getY() - p2.getY());
len = sqrt(x * x + y * y);
}
//组合类的复制构造函数
Line::Line(Line& l) :p1(l.p1), p2(l.p2) {
cout << "Calling the copy constructor of Line" << endl;
len = l.len;
}
//主函数
int main() {
Point myp1(1, 1), myp2(4, 5); //建立Point类的对象
Line line(myp1, myp2); //建立Line类的对象
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;
}
程序运行结果
分析
程序从main函数开始执行,当执行至Point myp1(1, 1), myp2(4, 5);
时,由于生成了两个Point类的对象myp1
、myp2
,调用Point类构造函数两次,对应程序运行结果中前两行Calling constructor of Point
(由蓝色1、2标出)。
构造函数调用条件:在对象被创建时自动调用
main函数继续执行,至Line line(myp1, myp2);
,这里比较复杂,其结果对应程序运行结果中用红色标出的1~4以及绿色的1,即执行Point类复制构造函数四次,执行Line类构造函数一次。下面来逐一分析为什么。
首先明确复制构造函数的三个调用条件。
复制构造函数调用条件:
(1)当用类的一个对象去初始化该类的另一个对象时
(2)如果函数的形参是类的对象,调用函数时,进行实参和形参结合时
(3)如果函数的返回值是类的对象,函数执行完成返回调用者时
现在,回过头看这行代码Line line(myp1, myp2);
1.由于在创建一个Line类的对象line,符合构造函数调用条件,所以会调用Line类的构造函数,又由于Line类的构造函数的形参是Point类的对象xp1、xp2,故满足复制构造函数调用条件中的(2),所以调用Line类的构造函数进行形参实参结合时,会调用Point类的复制构造函数两次(对应红色1、2)。
2.Line类构造函数的初始化列表为p1(xp1), p2(xp2)
,是在用Point类的一个对象xp1(xp2)去初始化Point类的另一个对象p1(p2),符合复制构造函数调用条件中的(1),故会调用Point类的复制构造函数两次(对应红色的3、4)。
3.Line类构造函数继续执行,控制台打印出Calling constructor of Line
,表示调用了一次Line类的复制构造函数(对应绿色的1)。
main函数继续执行,至Line line2(line);
,这是在用Line类的一个对象line去初始化Line类的另一个对象line2,符合复制构造函数调用条件中的(1),故这里会调用一次Line类的复制构造函数,又由于Line类的复制构造函数的初始化列表为p1(l.p1), p2(l.p2)
,是在用Point类的一个对象l.p1(l.p2)去初始化Point类的另一个对象p1(p2),符合复制构造函数调用条件中的(1),所以这里会调用Point类的复制构造函数两次(对应红色的5、6)。
Line类的复制构造函数继续执行,控制台打印出Calling the copy constructor of Line
,表示调用了一次Line类的复制构造函数(对应橙色的1)。
然后main函数继续执行,后边没有调用构造函数及复制构造函数的地方了,不在讲解。
总结
对于组合类中构造函数及复制构造函数的调用,主要是要牢记并且充分理解。
在创建一个组合类的对象时,不仅它自身的构造函数的函数体将被执行,而且还将调用其内嵌对象的构造函数。这时构造函数调用顺序如下:
(1)调用内嵌对象的构造函数,调用顺序按照内嵌对象在组合类的定义中出现的次序。注意,内嵌对象在构造函数的初始化列表中出现的顺序与内嵌对象构造函数的调用顺序无关。
(2)执行本类的构造函数函数体。
注:析构函数的调用执行顺序与构造函数刚好相反。