问题描述

有代码如下。请运行程序,并按照要求完成如下任务。

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. 程序的运行结果与内存分配情况

  1. 调用构造函数Point(int a,int b)创建两个Point对象:mp1mp2

    • 对于mp1(1,1),输出:构造了一个点(1,1)
    • 对于mp2(16,16),输出:构造了一个点(16,16)
  2. 调用构造函数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)使用了初始化列表。

    具体过程如下:

    1. 在创建Line对象时,由于按值传递,mp1mp2被拷贝到构造函数的形参pp1pp2。输出:

      拷贝构造了一个点(16,16)
      拷贝构造了一个点(1,1)
    2. 初始化列表中p1(pp1)p2(pp2)又各引发了一次拷贝构造调用,以初始化Line对象的p1p2成员。输出:

      拷贝构造了一个点(1,1)
      拷贝构造了一个点(16,16)
    3. 输出构造了一条线段

  3. 调用line.display()方法。

    • 输出:线段的起点,随后p1.show()输出:横坐标:1,纵坐标:1
    • 输出:线段的终点,随后p2.show()输出:横坐标:16,纵坐标:16
  4. 调用line.length()方法。

    • 输出线段的长度是21.2132
  5. 调用line.slope()方法。

    • 输出:线段的斜率是1

内存中的分配情况如动图所示:

内存分配动图

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)

可以看到,当对象的生命周期结束时,会自动调用析构函数。

  1. 由于构造函数Line(Point pp1, Point pp2) : p1(pp1), p2(pp2)为值传递,故在函数执行完后形参被销毁,输出:

    销毁了一个点(1,1)
    销毁了一个点(16,16)
  2. Line对象的析构,输出:

    销毁了一条线段
    销毁了一个点(16,16)
    销毁了一个点(1,1)
  3. Point对象的析构,输出:

    销毁了一个点(16,16)
    销毁了一个点(1,1)

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),并且也不需要销毁临时变量。

以下是程序运行动图:
gif