喜欢的朋友可以关注收藏一下: 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;
}