问题描述
有代码如下。请运行程序,并按照要求完成如下任务。
1、请分析代码的运行结果,并画出程序运行期间,内存分配情况;
2、请在程序中添加Point
的析构函数和Line
的析构函数。在两个析构函数中只需要进行信息输出即可。请给出修改后的完整代码,并将自己添加的代码用红色标出;
3、运行加入析构函数后的代码,分析其运行结果,并说明其与未加析构函数的时候的结果的不同之处,并说明原因;
4、请将程序中语句Line(Point pp1,Point pp2):p1(pp1),p2(pp2)
,修改为Line(Point &pp1,Point &pp2):p1(pp1),p2(pp2)
。然后运行程序,说明修改前后程序的运行结果区别及其原因。
#include <iostream> #include <math.h> using namespace std;class Point {public : Point (int a,int b) { x=a; y=b; cout<<"构造了一个点" <<"(" <<x<<"," <<y<<")" <<endl; } int Getx () { return x; } int Gety () { return y; } void show () { cout<<"横坐标:" <<x<<",纵坐标:" <<y<<endl; } private : int x,y; }; class Line {private : Point p1,p2; public : Line (Point pp1,Point pp2):p1 (pp1),p2 (pp2) { cout<<"构造了一条线段" <<endl; } void length () { double len; int x1,y1,x2,y2; x1=p1.Getx (); y1=p1.Gety (); x2=p2.Getx (); y2=p2.Gety (); len=sqrt ((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); cout<<"线段的长度是" <<len<<endl; } void slope () { double k; int x1,y1,x2,y2; x1=p1.Getx (); y1=p1.Gety (); x2=p2.Getx (); y2=p2.Gety (); k=(y2-y1)/(x2-x1); cout<<"线段的斜率是" <<k<<endl; } void display () { cout<<"线段的起点" ; p1.show (); cout<<"线段的终点" ; p2.show (); } }; int main () { Point mp1 (1 ,1 ) ,mp2 (16 ,16 ) ; Line line (mp1,mp2) ; line.display (); line.length (); line.slope (); }
运行后输出
构造了一个点(1,1) 构造了一个点(16,16) 构造了一条线段 线段的起点横坐标:1,纵坐标:1 线段的终点横坐标:16,纵坐标:16 线段的长度是21.2132 线段的斜率是1
1. 程序的运行结果与内存分配情况
调用构造函数Point(int a,int b)
创建两个Point
对象:mp1
和mp2
。
对于mp1(1,1)
,输出:构造了一个点(1,1)
对于mp2(16,16)
,输出:构造了一个点(16,16)
调用构造函数Line(Point &pp1,Point &pp2):p1(pp1),p2(pp2)
创建一个Line
对象line
。
事实上这里的值传递调用了默认的拷贝构造函数,要想打印日志可以在class Point:public
中添加拷贝构造函数:
Point (const Point &p) { x = p.x; y = p.y; cout << "拷贝构造了一个点(" << x << "," << y << ")" << endl; }
此时会输出:
拷贝构造了一个点(16,16) 拷贝构造了一个点(1,1) 拷贝构造了一个点(1,1) 拷贝构造了一个点(16,16) 构造了一条线段
可以看到这两个点分别被高倍构造了两次,这是由于构造函数Line(Point &pp1,Point &pp2):p1(pp1),p2(pp2)
使用了初始化列表。
具体过程如下:
在创建Line
对象时,由于按值传递,mp1
和mp2
被拷贝到构造函数的形参pp1
和pp2
。输出:
拷贝构造了一个点(16,16) 拷贝构造了一个点(1,1)
初始化列表中p1(pp1)
和p2(pp2)
又各引发了一次拷贝构造调用,以初始化Line
对象的p1
和p2
成员。输出:
拷贝构造了一个点(1,1) 拷贝构造了一个点(16,16)
输出构造了一条线段
调用line.display()
方法。
输出:线段的起点
,随后p1.show()
输出:横坐标:1,纵坐标:1
输出:线段的终点
,随后p2.show()
输出:横坐标:16,纵坐标:16
调用line.length()
方法。
调用line.slope()
方法。
内存中的分配情况如动图所示:
2. 添加析构函数
代码如下:
#include <iostream> #include <math.h> using namespace std;class Point {public : Point (int a, int b) { x = a; y = b; cout << "构造了一个点" << "(" << x << "," << y << ")" << endl; } Point (const Point &p) { x = p.x; y = p.y; cout << "拷贝构造了一个点(" << x << "," << y << ")" << endl; } ~Point () { cout << "销毁了一个点" << "(" << x << "," << y << ")" << endl; } int Getx () { return x; } int Gety () { return y; } void show () { cout << "横坐标:" << x << ",纵坐标:" << y << endl; } private : int x, y; }; class Line {private : Point p1, p2; public : Line (Point pp1, Point pp2) : p1 (pp1), p2 (pp2) { cout << "构造了一条线段" << endl; } ~Line () { cout << "销毁了一条线段" << endl; } void length () { double len; int x1, y1, x2, y2; x1 = p1.Getx (); y1 = p1.Gety (); x2 = p2.Getx (); y2 = p2.Gety (); len = sqrt ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); cout << "线段的长度是" << len << endl; } void slope () { double k; int x1, y1, x2, y2; x1 = p1.Getx (); y1 = p1.Gety (); x2 = p2.Getx (); y2 = p2.Gety (); k = (double )(y2 - y1) / (x2 - x1); cout << "线段的斜率是" << k << endl; } void display () { cout << "线段的起点" ; p1.show (); cout << "线段的终点" ; p2.show (); } }; int main () { Point mp1 (1 , 1 ) , mp2 (16 , 16 ) ; Line line (mp1, mp2) ; line.display (); line.length (); line.slope (); }
3. 运行加入析构函数后的代码
运行后输出:
构造了一个点(1,1) 构造了一个点(16,16) 拷贝构造了一个点(16,16) 拷贝构造了一个点(1,1) 拷贝构造了一个点(1,1) 拷贝构造了一个点(16,16) 构造了一条线段 销毁了一个点(1,1) 销毁了一个点(16,16) 线段的起点横坐标:1,纵坐标:1 线段的终点横坐标:16,纵坐标:16 线段的长度是21.2132 线段的斜率是1 销毁了一条线段 销毁了一个点(16,16) 销毁了一个点(1,1) 销毁了一个点(16,16) 销毁了一个点(1,1)
可以看到,当对象的生命周期结束时,会自动调用析构函数。
由于构造函数Line(Point pp1, Point pp2) : p1(pp1), p2(pp2)
为值传递,故在函数执行完后形参被销毁,输出:
Line
对象的析构,输出:
销毁了一条线段 销毁了一个点(16,16) 销毁了一个点(1,1)
Point
对象的析构,输出:
4. 修改Line
构造函数的参数为引用类型
将Line(Point pp1,Point pp2):p1(pp1),p2(pp2)
,修改为Line(Point &pp1,Point &pp2):p1(pp1),p2(pp2)
运行后输出:
构造了一个点(1,1) 构造了一个点(16,16) 拷贝构造了一个点(1,1) 拷贝构造了一个点(16,16) 构造了一条线段 线段的起点横坐标:1,纵坐标:1 线段的终点横坐标:16,纵坐标:16 线段的长度是21.2132 线段的斜率是1 销毁了一条线段 销毁了一个点(16,16) 销毁了一个点(1,1) 销毁了一个点(16,16) 销毁了一个点(1,1)
可以看到,使用引用方法后,构造函数Line(Point &pp1,Point &pp2):p1(pp1),p2(pp2)
在创建pp1
,pp2
时不会调用拷贝构造函数Point(const Point &p)
,所以只是在初始化列表中调用了拷贝构造函数Point(const Point &p)
,并且也不需要销毁临时变量。
以下是程序运行动图: