喜歡的朋友可以關注收藏一下: http://blog.csdn.net/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;
}