【原创】C++_第二周_Rectangle类的初步实现(一)

本文介绍了如何在C++中实现Rectangle类,包括构造函数、拷贝构造函数、赋值运算符和析构函数。重点讨论了深度拷贝在处理Point指针成员时的必要性,以及在解决头文件冲突和防止自我赋值错误时的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

喜欢的朋友可以关注收藏一下:  http://blog.youkuaiyun.com/qq_31201973

本文实现了一个Rectangle类型,原版要求如下:

为 Rectangle 类实现构造函数,拷贝构造函数,赋值操作符,析构函数。

 

class Shape
{                   
   int no;
};              
class Point
{
   int x;
   int y;
};              
class Rectangle: public Shape
{
   int width;
   int height;
   Point * leftUp;
public:
   Rectangle(int width, int height, int x, int y);
   Rectangle(const Rectangle& other);
   Rectangle& operator=(const Rectangle& other);
   ~Rectangle();         
};

  首先我的整体规划是class类的声明及对他的操作函数都写在头文件里,cpp里只放main()函数。而不是通常我们常见的声明放在头文件,cpp里放定义和main()函数。这么做的理由是我把整个class Rectangle类当做一个抽象封装,里面有对它的定义、操作、和依赖,我的程序里因为时间关系并没有写进位的依赖关系,但留下了扩展的余地,待以后扩展后再给大家分享后续操作。考虑到头文件中包含的头文件,和cpp中包含的头文件冲突,我采用了以下保护措施。

 

#ifndef __DATE_H__  
#define __DATE_H__  
  
  
  
#endif  

Rectangle类是由shape类派生出来的类该类包含三个成员变量width宽,height高,Point类型的指针变量leftUp (leftUp包含两个成员x,y为坐标),因为三个成员变量为private,外部函数无法访问他,所以我给Rectangle定义了三个成员函数get_width() get_height() get_leftUp(),返回width,height,Point的值。同时也给Point定义两个成员函数get_x() get_y() ,来返回x,y的值。

 

 

	class Point
	{
	private:
		int x;
		int y;
	public:
		int get_x() const { return x; }
		int get_y() const { return y; }
	};
	class Rectangle : public Shape
	{
	private:
		int width;
		int height;
		Point * leftUp;
	public:
		Rectangle(int width1, int height1, int x1, int y1);			//构造函数	
		Rectangle(const Rectangle& other);							//拷贝构造函数
		Rectangle& operator=(const Rectangle& other);				//拷贝赋值函数
		~Rectangle();												//析构函数 只声明不定义会出无法解析的错误
		int get_width() const { return width; }
		int get_height() const { return height; }
		Point* get_leftUp() const { return leftUp; }
	};

  然后就是为Rectangle类定义构造函数,构造函数是定义的时候为对象赋初值,我定义了两个构造函数分为普通构造函数和拷贝构造函数。

 

1.普通构造函数 a(),就是用默认参数构造。

2.拷贝构造函数 a(b),就是先构造b,把b的参数传给a构造a 。

  先来说普通构造函数,我采用初始化列表的方式初始化,前两个赋值没有什么问题,第三个成员是Point的指针你开始我用leftUp->x(x1);  方法赋值,结果发现成员变量不指明默认为private,如果用 leftUp->get_x() 来访问的话会因为返回的为值不能作为左值,所以我采用了深度拷贝的方法:leftUp(new Point(x1, y1))  ,因为用到了Point的构造,我为Point写了一个构造函数。

 

Point(int x1, int y1) : x(x1), y(y1)	{  }	//	构造函数,支持指针类型深度拷贝

这样Rectangle类的构造函数就可以正常初始化了

 

 

	inline Rectangle::Rectangle(int width1, int height1, int x1, int y1) : width(width1), height(height1), leftUp(new Point(x1, y1))	//深度拷贝,需要Point有构造函数
	{
		;
	}

然后为了测试Rectangle类的构造函数,我定义了一个<<重载函数

 

 

ostream& operator<<(ostream& os, guo::Rectangle& str)
{
	return os << '(' << str.get_height() << ',' << str.get_width() << ',' << str.get_leftUp()->get_x() << ',' << str.get_leftUp()->get_y() << ')' << endl;
}

然后测试的时候出现了这样的错误:

 



原因是windows.h里和Rectangle冲突,最初因为vs显示窗口一闪而过,我采用 system("pause"); 停一下。

解决方法有两种:

1.用getchar()代替system("pause") 。

2.把所有的类定义到命名空间中

 

namespace guo
{

}

这个问题解决以后又出现了新的问题:

 


这个问题的原因是只在类中声明了析构函数,没有定义它,造成无法解析的错误,解决完这个问题后就可以正常使用了。

 

#include<iostream>
#include"Rectangle.h"
#include<Windows.h>			//这个头文件中包含类似Rectangle的东西,会使Rectangle无法定义对象


using namespace std;
//using guo::Point;
//using guo::Shape;
//using guo::operator<<;

ostream& operator<<(ostream& os, guo::Rectangle& str)
{
	return os << '(' << str.get_height() << ',' << str.get_width() << ',' << str.get_leftUp()->get_x() << ',' << str.get_leftUp()->get_y() << ')' << endl;
}

int main()
{
	guo::Rectangle r1(1,1,1,1);		//构造函数定义法
	cout <<"r1="<< r1;
	system("pause");			//头文件 Windows.h ,因为vs2013不会自动添加增加  mov ah,1  int 21H mov ax,4C00H  int 21H   所以屏幕一闪而过。
	//getchar();				//也可以用这个函数等待输入一个字符来让程序等待,缺点是如果有输入,很容易不好用,所以建议用命名空间解决该问题
	return 0;
}

然后定义析构函数,析构函数是对象声明周期结束的时候调用。因为深度拷贝采用的是堆内存,堆内存是自定义的需要用delete释放,如果指向该内存的指针为栈内存变量,而栈内存变量生命周期结束以后由系统收回,那么堆内存没有指向的指针,该内存无法收回就造成了内存泄漏,该析构函数只用调用一次delete就可以。

 

 

	inline Rectangle::~Rectangle()
	{
		delete[] leftUp;
	}

之后定义拷贝构造函数,leftUp我采用初始化列表的方式,width  height 在因为未知原因无法用初始化列表,所以我在函数体内定义。

 

 

	inline Rectangle::Rectangle(const Rectangle& other) : leftUp(new Point(other.leftUp->get_x(), other.leftUp->get_y()))		//深度拷贝,需要Point有构造函数
	{
		width = other.width;
		height = other.height;
	}

测试:

 

 

	guo::Rectangle r1(1,1,1,1);		//构造函数定义法
	guo::Rectangle r2(2, 2, 1, 1);		//构造函数定义法
	guo::Rectangle r3(r2);		//拷贝构造函数定义法
	guo::Point p1(1, 2);		//构造函数定义法

	//cout << r1.get_height();

	cout <<"r1="<< r1;
	cout << "r2=" << r2;
	cout << "r3=" << r3;

最后定义拷贝赋值函数,分为三步:

 

1.delete原空间

2.this->leftUp用new定义一个和Point类一样大小的空间并赋值

3.给 width  height  赋值

但是要考虑要拷贝赋值的对象和原对象相同,这样会自我赋值会把自己给delete掉,从而出错。所以专门为这块写一段代码:

 

if (this == &other)		//重复自我赋值
{
	return *this;
}

这样拷贝赋值函数就写好了

 

 

	inline Rectangle& Rectangle::operator=(const Rectangle& other)				//拷贝赋值函数
	{
		if (this == &other)		//重复自我赋值
		{
			return *this;
		}

		delete this->leftUp;
		this->leftUp = new Point(other.leftUp->get_x(), other.leftUp->get_y());
		this->width = other.width;
		this->height = other.height;
		return *this;
	}

测试拷贝赋值函数:

 

 

	r1 = r3;

	cout << "r1=r3(拷贝赋值测试)" << endl;
	cout << "r1=" << r1;						//拷贝赋值测试

这样Rectangle类的基本创建功能就定义好了,下周再来实现其他功能,最后放一下所以代码。

 

 

//	本文件名  Rectangle.h			我的编程环境VS2013
/*		本程序的注释代表仅代表个人理解,不一定完全正确,全是我亲自敲上去的,如有错误请联系我						*/

#ifndef __RECTANGLE_H__
#define __RECTANGLE_H__
#include<iostream>
namespace guo
{
	class Shape
	{
		int no;
	};
	class Point
	{
	private:
		int x;
		int y;
	public:
		Point(int x1, int y1) : x(x1), y(y1)	{  }	//	构造函数,支持指针类型深度拷贝
		int get_x() const { return x; }
		int get_y() const { return y; }
	};
	class Rectangle : public Shape
	{
	private:
		int width;
		int height;
		Point * leftUp;
	public:
		Rectangle(int width1, int height1, int x1, int y1);			//构造函数	
		Rectangle(const Rectangle& other);							//拷贝构造函数
		Rectangle& operator=(const Rectangle& other);				//拷贝赋值函数
		~Rectangle();												//析构函数 只声明不定义会出无法解析的错误
		int get_width() const { return width; }
		int get_height() const { return height; }
		Point* get_leftUp() const { return leftUp; }
	};

	inline Rectangle::Rectangle(int width1, int height1, int x1, int y1) : width(width1), height(height1), leftUp(new Point(x1, y1))	//深度拷贝,需要Point有构造函数
	{
		;
	}
	
	inline Rectangle::~Rectangle()
	{
		delete[] leftUp;
	}

	inline Rectangle::Rectangle(const Rectangle& other) : leftUp(new Point(other.leftUp->get_x(), other.leftUp->get_y()))		//深度拷贝,需要Point有构造函数
	{
		width = other.width;
		height = other.height;
	}

	inline Rectangle& Rectangle::operator=(const Rectangle& other)				//拷贝赋值函数
	{
		if (this == &other)		//重复自我赋值
		{
			return *this;
		}

		delete this->leftUp;
		this->leftUp = new Point(other.leftUp->get_x(), other.leftUp->get_y());
		this->width = other.width;
		this->height = other.height;
		return *this;
	}

}

#endif

 

 

//	本文件名  Rectangle.cpp			我的编程环境VS2013
/*		本程序的注释代表仅代表个人理解,不一定完全正确,全是我亲自敲上去的,如有错误请联系我						*/

#include<iostream>
#include"Rectangle.h"
#include<Windows.h>			//这个头文件中包含类似Rectangle的东西,会使Rectangle无法定义对象


using namespace std;
//using guo::Point;
//using guo::Shape;
//using guo::operator<<;

ostream& operator<<(ostream& os, guo::Rectangle& str)
{
	return os << '(' << str.get_height() << ',' << str.get_width() << ',' << str.get_leftUp()->get_x() << ',' << str.get_leftUp()->get_y() << ')' << endl;
}

int main()
{
	guo::Rectangle r1(1,1,1,1);		//构造函数定义法
	guo::Rectangle r2(2, 2, 1, 1);		//构造函数定义法
	guo::Rectangle r3(r2);		//拷贝构造函数定义法
	guo::Point p1(1, 2);		//构造函数定义法

	//cout << r1.get_height();

	cout <<"r1="<< r1;
	cout << "r2=" << r2;
	cout << "r3=" << r3;

	r1 = r3;

	cout << "r1=r3(拷贝赋值测试)" << endl;
	cout << "r1=" << r1;						//拷贝赋值测试


	system("pause");			//头文件 Windows.h ,因为vs2013不会自动添加增加  mov ah,1  int 21H mov ax,4C00H  int 21H   所以屏幕一闪而过。
	//getchar();				//也可以用这个函数等待输入一个字符来让程序等待,缺点是如果有输入,很容易不好用,所以建议用命名空间解决该问题
	return 0;
}


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

#include <iostream> #include <string> using namespace std; struct CPoint { int x ; int y ; }; class CRectangle { private: const int id;//常量数据成员 static int total;//静态数据成员 const static string sclass; const static int f=1.0f; CPoint lefttop ; CPoint rightdown ; public: CRectangle( ); CRectangle( CPoint& lt, CPoint& rd ); CPoint GetLefttop() const { return lefttop; } CPoint GetRightdown() const { return rightdown; } void SetLefttop(CPoint & pt) { lefttop=pt; } void SetRightdown(CPoint & pt) { rightdown=pt; } int Getid() const { return id; } static int Gettotal() { return total; } int Area( ) const; int Perimeter( ) const; }; int CRectangle::total=0;//静态数据成员必须在的外部定义(正好次)。 const string CRectangle::sclass="CRectangle"; CRectangle::CRectangle( ):id(++total) { lefttop.x=0; lefttop.y=0; rightdown.x=1; rightdown.y=1; } CRectangle::CRectangle( CPoint& lt, CPoint& rd ):id(++total) { lefttop = lt ; rightdown = rd ; } int CRectangle::Area( ) const { int wd= rightdown.x - lefttop.x ; int ht= rightdown.y - lefttop.y ; return wd * ht ; } int CRectangle::Perimeter( ) const { int wd= rightdown.x - lefttop.x ; int ht= rightdown.y - lefttop.y ; return 2 * ( wd + ht ) ; } int main() { CPoint lt, rd; cin >> lt.x >> lt.y; cin >> rd.x >> rd.y; CRectangle crt(lt,rd);//调用有参构造函数 CRectangle crt2;//调用默认构造函数 //创建常量对象 const CRectangle crt3(lt,rd); cout<<"当前创建的矩形个数为:"; cout<<CRectangle::Gettotal()<<endl; //返回矩形的左上右下点 CPoint lt1=crt.GetLefttop(); CPoint lt2=crt.GetRightdown(); //显示矩形的坐标 cout<<crt.Getid()<<"号矩形的坐标是:"<<"("<<lt1.x<<","<<lt1.y<<"), "; cout<<"("<<lt2.x<<","<<lt2.y<<")"<<endl; //显示矩形的面积周长 cout << "Area:"<<crt.Area( )<<endl; cout <<"Perimeter:"<<crt.Perimeter( )<<endl; //修改矩形的左上角点 cout<<"请输入矩形新的左上点坐标:"; cin>> lt.x>>lt.y; crt.SetLefttop(lt); lt1=crt.GetLefttop(); //显示修改后矩形的坐标 cout<<"矩形的坐标是:"<<"("<<lt1.x<<","<<lt1.y<<"), "; cout<<"("<<lt2.x<<","<<lt2.y<<")"<<endl; //显示修改后矩形的面积周长 cout << "Area:"<<crt.Area( )<<endl; cout <<"Perimeter:"<<crt.Perimeter( )<<endl; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值