【问答】2022年9月16日C++课后作业——类组合

使用C++实现一个矩形类根据矩形左下角和右上角两个坐标求出面积

一、题目

调试下列程序,写出输出结果,并分析输出结果。

//Point.h文件
#ifndef POINT_H
#define POINT_H
class Point
{
	private:
		float x,y;
	public:
		Point();
		Point(int xx,int yy);
		Point(Point &p); 
		float getX();
		float getY(); 
};
#endif 
//Point.cpp文件
#include <iostream>
#include "Point.h"
using namespace std; 
Point::Point()
{
	x=0;
	y=0;
	cout<<"Point类的默认构造函数被调用"<<endl;
} 
Point::Point(int xx,int yy)
{
	x=xx;
	y=yy;
	cout<<"Point类的两个参数的构造函数被调用"<<xx<<","<<yy<<endl; 
} 
Point::Point(Point &p)
{
	x=p.x;
	y=p.y;
	cout<<"Point类的拷贝构造函数被调用"<<p.x<<","<<p.y<<endl;
}
float Point::getX()
{
	return x;
}
float Point::getY()
{
	return y;
}
//Rectangle.h文件
#include "Point.h" 
#ifndef RECTANGLE_H
#define RECTANGLE_H
class Rectangle
{
	private:
		Point p1;
		Point p2;
	public:
		Rectangle();
		Rectangle(Point xp1,Point xp2);
		Rectangle(Rectangle &rec);
		float computeArea();
};
#endif 
//Rectangle.cpp文件
#include <iostream>
#include "Rectangle.h"
#include <math.h> 
using namespace std;
Rectangle::Rectangle()
{
	cout<<"Rectangle类的默认构造函数被调用"<<endl;
}
Rectangle::Rectangle(Point xp1,Point xp2):p1(xp1),p2(xp2)
{
	cout<<"Rectangle类带有(Point,Point)的构造函数被调用"<<endl;
}
Rectangle::Rectangle(Rectangle &rec):p1(rec.p1),p2(rec.p2)
{
	cout<<"Rectangle类的拷贝构造函数被调用"<<endl;
}
float Rectangle::computeArea()
{
	float length=fabs(p2.getX()-p1.getX());
	float width=fabs(p2.getY()-p1.getY());
	return length*width;
} 
//main.cpp文件
#include <iostream>
#include "Rectangle.h"
using namespace std;
int main() {
	Point p3;
	Point p4(10,20);	
	Rectangle r1(p3,p4);
	cout<<"r1的面积为:"<<r1.computeArea()<<endl;
	cout<<"========================"<<endl;
	Rectangle r2=r1;
	cout<<"r2的面积为:"<<r2.computeArea()<<endl;
	return 0;
}
二、图形

image-20220916203538331

三、运行结果

四、分析结果

答:由题意可知Point类用于存储点坐标,并且Point在Rectangle类中定义的子对象用于进行矩形长宽计算,最后根据矩形面积计算,输出最后的矩形面积。

1、分析类的功能:
①在Point类:
定义了两个float的变量x,y
无参构造函数Point();功能:初始化点坐标(x,y)=(0,0)
两个参数的构造函数Point(int xx,int yy);
拷贝构造函数Point( Point &p);
获取x坐标函数float getX();
获取y坐标函数float getY();
②在Rectangle类:
类的组合,其中定义了p1,p2均为Point的子对象
无参构造函数Rectangle();
两个参数的构造函数Rectangle(Point xp1,Point xp2);
拷贝构造函数Rectangle( Rectangle &rec);
面积计算函数:float computeArea();

2、查看函数的特点:
本题目的主要点在于类的组合里Rectangle类构造函数初始化列表的值传递,以及默认构造函数的调用时机、拷贝构造函数调用时机。

3、main函数中:
Point p3; 这里只定义了p3对象,并没有赋值,在默认构造函数调用时机中:不指定类初始值时,会调用默认构造函数
显示输出:Point类的默认构造函数被调用

Point p4(10,20); 由于时值传递,因为DevC++编译器时从右到左的方式给形参赋值,系统先后创建出yy,xx接受传入的(10,20),然后形参的赋值给p3本身,局部对象xx,yy在函数执行完后释放,两个参数的构造函数Point(int xx,int yy);被调用完毕。
显示输出:Point类的两个参数的构造函数被调用

Rectangle r1(p3,p4);很明显会调用Rectangle类的两个参数的构造函数,这里p3,xp1,p1以及p4,xp2,p2的变化过程这里是分析的关键点:
我们要知道在以值传递的方式给函数参数传值的时候会调用拷贝构造函数:函数 Rectangle(Point xp1,Point xp2); 的参数是类Point 的对象,那么当 Rectangle(Point xp1,Point xp2); 被调用时,类 Point 的拷贝构造函数将被调用。作为形参的对象是用拷贝构造函数初始化的,而且调用拷贝构造函数时的参数就是调用Rectangle函数时所给的实参。换句话说在创建对象时既要对本类的基本类型数据成员进行初始化,又要对子对象成员进行初始化。所以在创建r1之前先要创建它的点类p1,p2,而他的Point类的对象的初始化是通过形参xp1,xp2传入的,而形参xp1,xp2为类对象,还是值传递,故系统要先创建出xp2,xp1两个局部Point类的变量来拷贝传入的点类p4,p3。
显示输出:
Point类的拷贝构造函数被调用10,20 (xp2(p4))
Point类的拷贝构造函数被调用0,0 (xp1(p3))
注意:如果形参列表中用引用的方式,是不会显示这两句话的。并且当我们提供了拷贝构造函数,系统就不会提供其他函数,所以在p3没有指定初始值的时候,不会调用默认构造函数,而是调用了拷贝构造函数。
那为什么会再次调用拷贝构造函数呢?因为xp1是Point对象,p1也是Point对象,用xp1去初始化p1。满足了拷贝函数调用时机:使用同一类中的对象去初始化另一个对象的条件,会再次调用拷贝构造函数。 然后再按照Rectangle类中定义的顺序先后初始化p1,p2
显示输出:
Point类的拷贝构造函数被调用0,0 (p1(xp1))
Point类的拷贝构造函数被调用10,20 (p2(xp2))
总之,要理清楚函数的实参与形参的传递方式,以及初始化的方式。其中分别为(xp2(p4))、(xp1(p3));(p1(xp1))、(p2(xp2))。也就是我实参会先传递参数给形参xp2,xp1,再用形参去初始化p1,p2。因为值传递的方式所以先给xp2再给xp1,又因为子对象的定义顺序是先p1再p2,所以初始化的顺序是先p1再p2。
最后待r1数据成员创建好了之后就执行函数体内的cout
显示输出:
Rectangle类带有(Point,Point)的构造函数被调用

cout<<“r1的面积为:”<<r1.computeArea()<<endl;,有值了就可以计算了
显示输出:
r1的面积为:200

显示输出:****========================

Rectangle r2=r1; 由于引用的方式(Rectangle &rec)传入r1,故不会启动拷贝构造再创建一个参数对象rec,而初始化是用了别名,但是还是同一个类的对象,故会调用拷贝构造函数。
显示输出:
Point类的拷贝构造函数被调用0,0 (p1(rec.p1))
Point类的拷贝构造函数被调用10,20 (p2(rec.p2))
Rectangle类的拷贝构造函数被调用
r2的面积为:200

五、精简

Point p3;这里只定义了p3对象,并没有赋值,在默认构造函数调用时机中:不指定类初始值时,会调用默认构造函数
Point p4(10,20);调用Point类中的两个参数的构造函数
Rectangle(Point xp1,Point xp2); 以值传递的方式给函数参数传值的时候会调用拷贝构造函数,且传递方向是从右向左,故先 xp2=p4 再 xp1=p3。然后在初始化p1和p2的过程中,xp1,xp2也是Point对象,所以使用同一类中的对象去初始化另一个对象的时候,会再次调用拷贝构造函数,而这次的顺序是p1,p2的定义顺序,故先p1=xp1后p2=xp2。
cout<<“r1的面积为:”<<r1.computeArea()<<endl;输出面积
cout<<“========================”<<endl;输出符号
Rectangle r2=r1;使用同一Rectangle 类中的对象去初始化另一个对象的时候调用拷贝构造函数,因为Rectangle(Rectangle &rec):p1(rec.p1),p2(rec.p2)中(Rectangle &rec)是引用的方式,不是值传递,所以不会调用拷贝构造函数。p1(rec.p1),p2(rec.p2)初始化要调用拷贝构造函数。
cout<<“r2的面积为:”<<r2.computeArea()<<endl;输出面积

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值