實習目標 | 課堂中舉了一個 Shape-Point-Circle-Cylinder 的不適當的繼承範例, 請綜合運用繼承, 組合的語法來設計較好的程式架構 |
---|---|
步驟一 |
不適當的繼承範例如下:
class Shape { public: virtual float area() const { return 0.0; } virtual float volume() const { return 0.0; } }; class Point: public Shape { public: Point(float x=0, float y=0, float z=0) { this->x = x; this->y = y; this->z = z; } float getX() const { return x; } float getY() const { return y; } float getZ() const { return z; } private: float x, y, z; }; class Circle: public Point { public: Circle(float r=0., float x=0., float y=0., float z=0.) :Point(x,y,z),radius(r){} float area() const { return 3.141159*radius*radius; } float getRadius() const { return radius; } private: float radius; }; class Cylinder: public Circle { public: Cylinder(float h=0., float r=0., float x=0., float y=0., float z=0.): Circle(r,x,y,z), height(h) {} float area() const { return 2*Circle::area()+height*2*3.14159*getRadius(); } float volume() const { return Circle::area()*height; } private: float height; }; void main() { Point point1(1,2,3), point2(4,5,6); Circle circle(5, 2,0,-2); Cylinder cylinder1(5, 3, 0,0,0), cylinder2(4, 2, 1,1,1); Shape *shapes[] = {&point1, &point2, &circle, &cylinder1, &cylinder2}; int i; for (i=0; i<5; i++) { shapes[i]->display(cout); cout << endl; cout << " area:" << shapes[i]->area(); cout << " volume:" << shapes[i]->volume() << endl; } }上面程式碼中類別的階層如下圖: |
步驟二 |
我們仔細審查所要製作的這幾個類別: Point, Circle, Cylinder,
希望重新組合這幾個物件的設計,
運用比較多的重用機制來避免程式碼的重複,
但是又不至於錯用繼承的機制。
我們發現 Circle 物件中應該可以用到一個 Point 物件來表達圓心所在, Cylinder 物件中應該可以用兩個 Circle 物件來表達上底及下底兩個圓。 另外如果希望在程式中以一致的方法來處理, 儲存這些圖形物件的話, 我們可以將共通的資料與界面抽出來成為 Shape 物件, 如下圖所示: |
步驟三 |
請修改類別的定義來完成上圖的架構
首先 Shape 類別的界面除了保留 area() 及 volume() 之外, 再加上 display(), 為了透過多型指標來操作 Shape 衍生類別的物件, 這些界面函式都宣告為虛擬函式 (virtual function), 同時因為這個類別為抽象的共同界面, 所以這些虛擬函式也定義為純粹的虛擬函式 (pure virtual function): class Shape { public: virtual float area() const=0; virtual float volume() const=0; virtual void display(ostream &os)=0; };雖然 area() 及 volume() 是純粹虛擬函式, 在 Shape.cpp 中我們還是可以實作 Shape::area() 以及 Shape::volume() 來容納共通的程式, 例如: float Shape::area() const { return 0.0; }如果衍生類別需要的話也可以呼叫這個實作。 |
步驟四 |
其次我們實作 Point, Circle, 和 Cylinder 三個類別,
每一個類別中都需要實作 area(), volume() 和 display()
三個函式才能夠作出該類別的實體物件。
各個類別運用其它類別來重用程式碼, 並且使用委託的機制來取代步驟一中繼承的程式界面。 例如: Circle 中定義 Point 物件 m_center, Circle 的建構元函式和 display() 函式如下: Circle::Circle(float r, float x, float y, float z) :m_center(x,y,z), m_radius(r) { } void Circle::display(ostream &os) { os << "Circle: radius=" << m_radius << " center:"; m_center.display(os); } |
步驟五 |
請用下面簡單的程式碼來測試相關的功能:
#include <iostream> using namespace std; #include "Point.h" #include "Circle.h" #include "Cylinder.h" void main() { Point point1(1,2,3), point2(4,5,6); Circle circle(5, 2,0,-2); Cylinder cylinder1(5, 3, 0,0,0), cylinder2(4, 2, 1,1,1); Shape *shapes[] = {&point1, &point2, &circle, &cylinder1, &cylinder2}; int i; for (i=0; i<5; i++) { shapes[i]->display(cout); cout << endl; cout << " area:" << shapes[i]->area(); cout << " volume:" << shapes[i]->volume() << endl; } }範例執行程式 執行結果範例: Point: [1,2,3] area:0 volume:0 Point: [4,5,6] area:0 volume:0 Circle: radius=5 center:Point: [2,0,-2] area:78.529 volume:0 Cylinder: height=5 topCircle:Circle: radius=3 center:Point: [0,0,0] bottomCircle:Circle: radius=3 center:Point: [0,0,5] area:150.789 volume:141.352 Cylinder: height=4 topCircle:Circle: radius=2 center:Point: [1,1,1] bottomCircle:Circle: radius=2 center:Point: [1,1,5] area:75.3947 volume:50.2585 |
步驟六 | 請助教檢查後, 將所完成的 project (去掉 debug/ 資料匣下的所有內容) 壓縮起來, 選擇 Lab14-2 上傳, 後面的實習課程可能需要使用這裡所完成的程式 |
回
C++ 物件導向程式設計課程
首頁
製作日期: 05/31/2004
by 丁培毅 (Pei-yih Ting)
E-mail: pyting@mail.ntou.edu.tw
TEL: 02 24622192x6615
海洋大學
電機資訊學院
資訊工程系
Lagoon