Download presentation
Presentation is loading. Please wait.
1
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
物件與類別 鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
2
綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列
3
綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列
4
三個矩形物件 b 2 a 5 2 c 1 3 4
5
旋轉90o 1 3 a c 4 5 b 2 2
6
RotatingRectangles0.Program.Main片段
int width_a = 3; int height_a = 5; // 矩形b int width_b = 2; int height_b = 2; // 矩形c int width_c = 4; int height_c = 1; // 旋轉90度 Rotate(ref width_a, ref height_a); Rotate(ref width_b, ref height_b); Rotate(ref width_c, ref height_c);
7
RotatingRectangles0.Program.Rotate
static void Rotate(ref int width, ref int height) { int temp = width; width = height; height = temp; }
8
討論 矩形a的寬與高,分開宣告為width_a與height_a 旋轉90o應該是矩形物件具有的功能,在程式中卻完全看不出來
靠註解才能知道它們屬於同一個矩形 旋轉90o應該是矩形物件具有的功能,在程式中卻完全看不出來 單看程式碼很難體會它代表將矩形旋轉90o 使用幾乎沒有限制 即使兩個參數並不代表某個矩形物件的寬與高,任何程式師還是可以透過呼叫函式Rotate,來交換任兩個整數變數的數值
9
矩形物件列表 狀態 功能 名稱 寬 高 功能1 a 3 5 旋轉90o b 2 c 4 1
10
旋轉90o後矩形物件列表 狀態 功能 名稱 寬 高 功能1 a 5 3 旋轉90o b 2 c 1 4
11
矩形物件與類別
12
C# Class Rectangle class Rectangle { public int width;
public int height; public void Rotate(){ int temp = width; width = height; height = temp; }
13
物件、類別 物件(Object) 類別(Class) 狀態與功能:例如, 矩形a, 電視機甲 藍圖:例如, 矩形類別, 泛稱電視機
狀態:成員變數(member variables) 如width, height 功能:成員函式(member function) 如Rotate
14
類別Rectangle之成員函式Rotate
功能為交換封裝一起的成員變數width和height之值 不需要由外界輸入參數 不用加上關鍵字static
15
RotatingRectangles.Program.Main 片段
Rectangle a = new Rectangle(); a.width = 3; a.height = 5; Rectangle b = new Rectangle(); b.width = 2; b.height = 2; Rectangle c = new Rectangle(); c.width = 4; c.height = 1; a.Rotate(); b.Rotate(); c.Rotate();
16
新增類別程式 專案>加入類別>類別 輸入類別名稱.cs
17
RotatingRectangles.Rectangle 框架程式碼
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace RotatingRectangles { class Rectangle }
18
封裝(Encapsulation) 類別將狀態變數與功能函式封裝起來 函式導向的程式也可將函式當作黑箱模組 使類別內容的修改更容易
若能把類別變成黑箱(black box)模組,使用的程式師不用了解成員變數與成員函式的細節,就能應用類別的功能 程式碼重複使用(code reuse) 函式導向的程式也可將函式當作黑箱模組 相關的變數沒有結合在一起 程式碼重複使用不是那麼方便
19
堆疊(Stack)與堆積(Heap) Heap . Stack
20
實值型別儲存方式 堆疊(Stack) int x = 100; x 100
21
參考型別儲存方式 堆積(Heap) 堆疊(Stack) string x = “abc”; x 參考 ‘a’ ‘b’ ‘c’
22
矩形物件記憶配置 堆積記憶區 堆疊記憶區 a b this a.width a.height b.width b.height
函式Rectangle.Rotate進入點 a.height b.height 堆疊記憶區 堆積記憶區
23
成員函式 同類別的物件,同一個成員函式只有一套函式程式碼 不同物件呼叫自己的成員函式,使用物件自己的成員變數
各物件就只存放這個程式碼進入點的位置 函式程式碼通常佔據相當大的記憶,如果每個物件都存放一套函式程式碼,需要很多記憶體 不同物件呼叫自己的成員函式,使用物件自己的成員變數 會把物件在堆積記憶區的地址(稱為this)也傳給成員函式 由this成員函式可知道如何取用對應的成員變數
24
private與public 一般電視機 類別 可以讓一般使用者調整的按鈕通常顯露在外
不讓使用者碰觸的電路裝置,都封裝在機殼內,對使用者來說無異黑箱 類別 相當於電視內部電路,作為自身工具幫手的成員變數與成員函式,宣告時加上關鍵字private,不讓外界程式師取用 像是電視的操控按鈕,提供給外界使用的成員變數與成員函式,則在宣告時加上關鍵字public 視為進一步的「封裝」
25
軟體IC 硬體IC 類別的封裝 內部的電路封裝在內,不輕易顯露
軟體工程師不需要了解private成員變數與成員函式 只要懂得public成員的使用,也能正確應用既有類別程式,達到減輕工作負荷,增進效率的目的
26
封裝 通常狀態變數為private ,只供同類別之功能函式取用,不可以由類別外直接取用
若有必要讓外界取得或設定其值,通常另外再寫public的成員函式來處理避開同名變數問題 避免被不慎改動,破壞狀態的正確一致性 容易維護,不影響外界程式 功能函式可能公用(public) ,也可能私有 一類別至少有一公用函式,以便應用
27
RotatingRectangles2.Rectangle片段 (1/2)
private int width; private int height; public void SetWidth( int w ) { width = w; } public int GetWidth() { return width; public void SetHeight(int h) { height = h;
28
RotatingRectangles2.Rectangle片段 (2/2)
public int GetHeight() { return height; } public void Rotate() { int temp = width; width = height; height = temp;
29
RotatingRectangles2.Program.Main片段 (1/2)
Rectangle a = new Rectangle(); a.SetWidth(3); a.SetHeight(5); Rectangle b = new Rectangle(); b.SetWidth(2); b.SetHeight(2); Rectangle c = new Rectangle(); c.SetWidth(4); c.SetHeight(1); a.Rotate(); b.Rotate(); c.Rotate();
30
RotatingRectangles2.Program.Main片段 (2/2)
Debug.Assert(a.GetWidth() == 5 && a.GetHeight() == 3); Debug.Assert(b.GetWidth() == 2 && b.GetHeight() == 2); Debug.Assert(c.GetWidth() == 1 && c.GetHeight() == 4);
31
偵錯器追踪 物件的成員變數值 「逐步執行」或按F11鍵,至進入類別 目前類別名稱、函式名稱 函式區域變數及參數 this及物件的成員變數值
32
綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列
33
設定或取得成員變數的值 通常將成員變數宣告為private,另外宣告public的成員函式來設定或取得成員變數的值,以維護成員變數間的一致性
類別中增加了很多只是要Set和Get成員變數值的簡單函式,使類別宣告有一點冗長,而呼叫這些函式時也得加上括弧,有一點累贅
34
屬性 (Attribute) 兼顧成員變數間的一致與程式寫法的簡潔
成員函式SetWidth和GetWidth可以整合為一個屬性Width,成員函式SetHeight和GetHeight可以整合為一個屬性Height 將屬性名稱取成與成員變數相同 唯一差別為開始字母之大小寫 屬性名稱也可以與成員變數不同 此處的作法可以省去找新名稱的麻煩,並使屬性與成員變數的關連清楚
35
RotatingRectangles3.Program.Main片段 (1/2)
Rectangle a = new Rectangle(); a.Width = 3; a.Height = 5; Rectangle b = new Rectangle(); b.Width = 2; b.Height = 2; Rectangle c = new Rectangle(); c.Width = 4; c.Height = 1; a.Rotate(); b.Rotate(); c.Rotate();
36
RotatingRectangles3.Program.Main片段 (2/2)
Debug.Assert(a.Width == 5 && a.Height == 3); Debug.Assert(b.Width == 2 && b.Height == 2); Debug.Assert(c.Width == 1 && c.Height == 4);
37
RotatingRectangles3.Rectangle片段
private int width; private int height; public int Width { set { width = value; } get { return width; } } public int Height { set { height = value; } get { return height; }
38
DiceSimulation.Program.Main片段
int[] count = { 0, 0, 0, 0, 0, 0 }; int N = 12000; Dice dice = new Dice(); for (int i = 0; i < N; ++i) { dice.Toss(); count[dice.FaceValue - 1]++; } for (int k = 0; k < 6; ++k) { Console.WriteLine(" {0} appears {1} times ", k + 1, count[k]);
39
DiceSimulation.Dice class Dice { private int faceValue = 1;
private Random rand = new Random(); public int FaceValue { get { return faceValue; } } public void Toss() { faceValue = rand.Next() % 6 + 1;
40
投擲兩顆骰子 非常可能發現兩顆骰子擲出的結果相同 dice1和dice2幾乎同時建立
Dice dice1 = new Dice(); Dice dice2 = new Dice(); dice1.Toss(); dice2.Toss(); Console.WriteLine( dice1.FaceValue ); Console.WriteLine( dice2.FaceValue ); 非常可能發現兩顆骰子擲出的結果相同 dice1和dice2幾乎同時建立 成員變數rand建立時,會使用相同的計算機時間做為亂數產生器的種子,使兩者得到的亂數數列完全相同
41
綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列
42
函式多載 (Overloading) 變數、函式、物件、類別都要選擇適切的名稱,程式才易寫易讀 FORTRAN語言函式庫的例子
適當的名稱常常不夠應用 FORTRAN語言函式庫的例子 讓計算實數參數與複數參數正弦函數的函式,雖然演算法不同,都命名為SIN 計算機自動依照參數型別的不同來決定該呼叫那一個函式 C++、Java、C#採相似的做法
43
分辨類別的成員函式 主要靠函式名稱,其次是參數的個數與型別 例:模擬時鐘運作 名稱相同,但是參數的個數或型別有所不同時,就可認為是不同的函式
時間的呈現可以是二十四小時制,也可以是區分上下午(AM、PM)的十二小時制 如果設定時間(SetTime)與取得時間(GetTime)的函式,對兩種不同時間呈現方式要取不同名稱,並不太容易,即使能夠,也會使函式名稱變得很長,不易使用
44
ClockSimulation.Program.Main片段
Clock c = new Clock(); c.SetTime(22, 40, 23); c.Tick(); c.GetTime(out h, out m, out s); Debug.Assert(h == 22 && m == 40 && s == 24); c.SetTime(11, 59, 59, "PM"); string aAMPM; c.GetTime( out h, out m, out s, out aAMPM ); Debug.Assert(h == 0 && m == 0 && s == 0 && aAMPM == "AM");
45
ClockSimulation.Program.Clock片段 (1/4)
private int hour = 0; // 0~23 private int minute = 0;// 0~59 private int second = 0;// 0~59 // 24小時制 public void SetTime( int h, int m, int s ) { SetValues( h, m, s ); } // 上下午制 public void SetTime( int h, int m, int s, string aAMPM ) { if( aAMPM == "PM" && h != 12 ) hour += 12;
46
ClockSimulation.Program.Clock片段 (2/4)
private void SetValues( int h, int m, int s ) { hour = h; minute = m; second = s; } // 24小時制 public void GetTime(out int h, out int m, out int s) { h = hour; m = minute; s = second;
47
ClockSimulation.Program.Clock片段 (3/4)
// 上下午制 public void GetTime(out int h, out int m, out int s, out string aAMPM) { h = hour; if (hour > 12) h -= 12; m = minute; s = second; if (hour >= 12) aAMPM = "PM"; else aAMPM = "AM"; }
48
ClockSimulation.Program.Clock片段 (4/4)
// 前進一秒 public void Tick() { second++; if (second == 60) { second = 0; minute++; } if (minute == 60) { minute = 0; hour++; if (hour == 24) hour = 0;
49
綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列
50
初始值設定 建立物件後,必需以屬性或成員函式指定成員變數的值 如果忘了,就發生錯誤 最好在建立物件時便能設定必要成員變數的初值
沒有設定成員變數初值的錯誤,通常稱為「初始值未設定」錯誤(initialization error) 最好在建立物件時便能設定必要成員變數的初值
51
RotatingRectangles4.Program.Main片段
Rectangle a = new Rectangle(3, 5); Rectangle b = new Rectangle(2); Rectangle c = new Rectangle(4, 1); a.Rotate(); b.Rotate(); c.Rotate(); Debug.Assert(a.Width == 5 && a.Height == 3); Debug.Assert(b.Width == 2 && b.Height == 2); Debug.Assert(c.Width == 1 && c.Height == 4);
52
RotatingRectangles4.Rectangle片段 (1/2)
private int width; private int height; public Rectangle() { width = 1; height = 1; } public Rectangle(int w, int h) { width = w; height = h; public Rectangle(int w) { height = w;
53
RotatingRectangles4.Rectangle片段 (2/2)
public int Width { get { return width; } } public int Height { get { return height; } public void Rotate() { int temp = width; width = height; height = temp;
54
DiceSimulation2.Program.Main 片段
int[] count = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int N = 12000; const int SEED_1 = 168; Dice dice1 = new Dice(SEED_1); const int SEED_2 = 777; Dice dice2 = new Dice(SEED_2); for (int i = 0; i < N; ++i) { dice1.Toss(); dice2.Toss(); count[ dice1.FaceValue + dice2.FaceValue - 2]++; }
55
DiceSimulation2.Dice片段
private int faceValue = 1; private Random rand; public Dice() { rand = new Random(); } public Dice(int seed) { rand = new Random(seed); public int FaceValue { get { return faceValue; } ~Dice() {}
56
解構式 (Destructor) ~Dice() {} 與建構式相對,用於物件將被消滅的時候
會將物件佔用的堆積記憶區使用權歸還給系統,以便系統可以把這些記憶體分配給其他物件使用 稱為停用記憶區收集(garbage collection) 把不再使用的資源釋出 例如,某個處理檔案的物件,在建構式中開啟檔案,開始應用,當物件要被消滅時,便需在解構式中關閉檔案,以便他處可以再使用同一檔案
57
記憶漏失 (Memory Leakage) 記憶區回收的工作可能相當複雜,處理不好,就容易有忘記回收的區域
忘記回收的區域逐漸累積,就可能使程式繼續執行所需的記憶體不足,稱為記憶漏失 先前的程式語言如C++,經驗不足的程式設計師很容易就寫出有記憶漏失的程式碼 較新的語言如Java、C#通常讓系統自動回收記憶區,因此解構式內就不必再寫回收記憶區的程式敘述
58
解構式 可看成特別的成員函式 一個類別只能有一個解構式 必須能被外界呼叫,所以不可加上public或private等修飾語
沒有回傳值,所以也不宣告回傳值型別
59
物件產生與消滅流程 static void main(string[] arg) {
Dice dice1 = new Dice(SEED_1); public Dice(int seed) rand = new Random(seed); } ~Dice(){} 宣告物件 建立物件 消滅物件dice1 Random建構式 }
60
綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列
61
物件宣告 在堆疊記憶區指定一個位址 接著以建構式建立物件 在堆積記憶區配置成員變數 填入成員變數初值
將堆積記憶區開始的位址放入堆疊記憶區的對應位置,成為物件的參考
62
this參考 堆疊記憶區有一個參考指到堆積記憶區 成員函式呼叫時會將參考this一併傳出 以便成員函式可以使用對應物件的成員變數
或用同樣方式呼叫其他成員函式
63
Rectangle物件記憶配置 堆積記憶區 堆疊記憶區 a b this a.width a.height b.width this
b.height 函式Rectangle.Rotate進入點
64
參數名稱 在成員函式或建構式中可以特別用this指出物件本身的成員變數 參數名稱便可以取成與成員變數名稱相同
65
RotatingRectangles5.Rectangle片段
private int width; private int height; public Rectangle(int width, int height) { this.width = width; this.height = height; } public Rectangle(int width) { this.height = width; public int Width { get { return width; }
66
要當心的情況 Rectangle.Scale
public void Scale(int s, int width, int height) { width *= s; height *= s; } Rectangle a = new Rectangle(3, 5); a.Scale(2, 3, 5);
67
綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列
68
不需物件的函式呼叫 例如兩個與時間轉換相關的函式HoursToMins和DaysToHours 可以寫成主程式裏的函式,供主程式呼叫
分別把小時數換算成分鐘、天數換算成小時 HoursToMins(5)為300(5小時等於300分鐘) DaysToHours(4)為96(4天等於96小時) 可以寫成主程式裏的函式,供主程式呼叫 這兩個函式就被綁在主程式裏,不容易給其他的類別程式使用
69
靜態成員函式 把函式連同它們要用到的常數,一起包在一個類別內 宣告時加註關鍵字static,表示是靜態成員函式,不會用到物件的成員變數
類別中宣告的常數不算成員變數 呼叫時,不需先建立物件,直接以類別名稱呼叫即可
70
ConvertingTime.TimeConversion
class TimeConversion { private const int HOURS_PER_DAY = 24; private const int MINS_PER_HOUR = 60; public static int HoursToMins(int hours) { return hours * MINS_PER_HOUR; } public static int DaysToHours(int days) { return days * HOURS_PER_DAY;
71
ConvertingTime.Program.Main片段
Debug.Assert(TimeConversion.HoursToMins(5) == 300); Debug.Assert(TimeConversion.DaysToHours(4) == 96); Console.Write("請輸入要轉換的小時數(整數): "); int hours = int.Parse(Console.ReadLine()); Console.Write("請輸入要轉換的天數(整數): "); int days = int.Parse(Console.ReadLine()); int hoursToMins = TimeConversion.HoursToMins(hours); int daysToHours = TimeConversion.DaysToHours(days);
72
靜態成員函式 主程式Main的執行,並不需要用到Program物件的成員變數
數學函數,如Exp、Log、Sin、Cos等,也都是系統類別Math的靜態成員函式 要以類別名稱Math呼叫
73
計算物件產生的個數 可以在主程式中放一個計數器,每建立一個物件就累加一次
如果主程式外的其他類別成員函式執行時,也會產生物件,主程式中的計數器就算不到這些另外建立的物件 物件計數器最好還是放在類別中 讓類別自己統計已建立的物件總數
74
CountingRectangles.Program.Main static void Main(string[] args) {
Rectangle a = new Rectangle(3, 5); Rectangle b = new Rectangle(2); Rectangle c = new Rectangle(4, 1); Rectangle d = new Rectangle(); Debug.Assert( Rectangle.GetNConstructed() == 4); Debug.Assert(Rectangle.NRectangles == 4); }
75
CountingRectangles.Rectangle片段 (1/2)
private int width; private int height; private static int nConstructed = 0; public Rectangle() { width = 1; height = 1; nConstructed++; } public Rectangle(int width, int height) { this.width = width; this.height = height;
76
CountingRectangles.Rectangle片段 (2/2)
public Rectangle(int width) { this.width = width; this.height = width; nConstructed++; } public static int GetNConstructed() { return nConstructed; public static int NRectangles { get { return nConstructed; }
77
Rectangle物件記憶配置 a b 堆疊記憶區 堆積記憶區 this a.width a.height this b.width
b.height 類別Rectangle成員函式 Rectangle.nConstructed 預設建構式進入點 建構式1進入點 建構式2進入點 函式Rectangle.GetNConstructed進入點 屬性Width進入點 屬性Height進入點 屬性Rectangle.NRectangles進入點 函式Rotate進入點
78
綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列
79
UsingStruct.Point2D struct Point2D { public int x; public int y; }
80
UsingStruct.Program.Main static void Main(string[] args) {
Point2D pt = new Point2D(); Console.WriteLine( "Initial location = ({0},{1})", pt.x, pt.y); pt.x = 3; pt.y = 4; "Final location = ({0},{1})", pt.x, pt.y); }
81
類別與結構的記憶配置 由類別產生的物件放一個參考在堆疊區 由結構建立的物件把所有的成員變數與成員函式進入點都存放在堆疊區
指到堆積記憶區的成員變數的開始位置 由結構建立的物件把所有的成員變數與成員函式進入點都存放在堆疊區
82
StructVSClass.SPoint2D
struct SPoint2D { public int x; public int y; public SPoint2D(int x, int y) { this.x = x; this.y = y; }
83
StructVSClass.CPoint2D
public int x; public int y; public CPoint2D() { x = 0; y = 0; } public CPoint2D(int x, int y) { this.x = x; this.y = y;
84
StructVSClass.Program.Main片段 (1/4)
SPoint2D sPt1 = new SPoint2D(3, 4); SPoint2D sPt2 = new SPoint2D(); SPoint2D sPt3 = new SPoint2D(); sPt2 = sPt1; sPt3 = sPt1; Console.WriteLine("sPt1 = ({0}, {1})", sPt1.x, sPt1.y); Console.WriteLine("sPt2 = ({0}, {1})", sPt2.x, sPt2.y); Console.WriteLine("sPt3 = ({0}, {1})", sPt3.x, sPt3.y);
85
StructVSClass.Program.Main片段 (2/4)
CPoint2D cPt1 = new CPoint2D(3, 4); CPoint2D cPt2 = new CPoint2D(); CPoint2D cPt3 = new CPoint2D(); cPt2 = cPt1; cPt3 = cPt1; Console.WriteLine("cPt1 = ({0}, {1})", cPt1.x, cPt1.y); Console.WriteLine("cPt2 = ({0}, {1})", cPt2.x, cPt2.y); Console.WriteLine("cPt3 = ({0}, {1})", cPt3.x, cPt3.y);
86
StructVSClass.Program.Main片段 (3/4)
sPt1.x = 10; sPt1.y = 20; sPt2.x = 30; sPt2.y = 40; Console.WriteLine("sPt1 = ({0}, {1})", sPt1.x, sPt1.y); Console.WriteLine("sPt2 = ({0}, {1})", sPt2.x, sPt2.y); Console.WriteLine("sPt3 = ({0}, {1})", sPt3.x, sPt3.y);
87
StructVSClass.Program.Main片段 (4/4)
cPt1.x = 10; cPt1.y = 20; cPt2.x = 30; cPt2.y = 40; Console.WriteLine("cPt1 = ({0}, {1})", cPt1.x, cPt1.y); Console.WriteLine("cPt2 = ({0}, {1})", cPt2.x, cPt2.y); Console.WriteLine("cPt3 = ({0}, {1})", cPt3.x, cPt3.y);
88
結構物件記憶配置 堆疊記憶區 this this this sPt1.x sPt1.y sPt2.x sPt2.y sPt3.x
建構式SPoint2D進入點
89
類別物件記憶配置 堆疊記憶區 堆積記憶區 this cPt1.x cPt1 cPt1.y this cPt2.x cPt2.y cPt2
預設建構式CPoint2D進入點 建構式CPoint2D進入點
90
練習 宣告並測試結構Student,其中包括學號、姓名、成績
91
綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列
92
類別物件設值 僅將堆疊記憶區的物件參考設成相同
不改變成員變數之值 使原先不同的物件綁在一起,成為相同物件,容易產生語義錯誤 若程式有造成這種語義錯誤的可能,應該在設值時使用複製建構式(copy constructor)
93
UsingCopyConstructor.Program.Main 片段 (1/2)
CPoint2D cPt1 = new CPoint2D(3, 4); CPoint2D cPt2 = new CPoint2D( cPt1 ); CPoint2D cPt3 = new CPoint2D( cPt1 ); Console.WriteLine("cPt1 = ({0}, {1})", cPt1.x, cPt1.y); Console.WriteLine("cPt2 = ({0}, {1})", cPt2.x, cPt2.y); Console.WriteLine("cPt3 = ({0}, {1})", cPt3.x, cPt3.y);
94
UsingCopyConstructor.Program.Main 片段 (2/2)
cPt1.x = 10; cPt1.y = 20; cPt2.x = 30; cPt2.y = 40; Console.WriteLine("cPt1 = ({0}, {1})", cPt1.x, cPt1.y); Console.WriteLine("cPt2 = ({0}, {1})", cPt2.x, cPt2.y); Console.WriteLine("cPt3 = ({0}, {1})", cPt3.x, cPt3.y);
95
UsingCopyConstructor.CPoint2D片段
public CPoint2D() { x = 0; y = 0; } public CPoint2D(int x, int y) this.x = x; this.y = y; public CPoint2D(CPoint2D p) x = p.x; y = p.y;
96
UsingCopyConstructor執行後之 記憶配置
堆積記憶區 堆疊記憶區 cPt1 this cPt1.x cPt1.y cPt2 this cPt3 this 建構式CPoint2D進入點 複製建構式CPoint2D進入點 cPt1.x cPt1.y cPt1.x cPt1.y
97
淺層複製與深層複製 淺層複製(Shallow copy) (設值敘述) 深層複製(Deep copy) (複製建構式) 系統提供
只複製參考(Reference)地址 沒有新物件產生 深層複製(Deep copy) (複製建構式) 程式師提供 產生新物件 應複製所有資料成員
98
練習 宣告並測試類別Student,其中包括學號、姓名、成績,仿照程式StructVSClass及UsingCopyConstructor分別使用設值與複製建構函式產生Student物件,觀察淺層複製與深層複製的差別
99
綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列
100
SwappingRectangles.Program.Main (非正確版本)
static void Main(string[] args) { Rectangle a = new Rectangle(3, 5); Rectangle b = new Rectangle(2); Swap(a, b); Debug.Assert( a.Width == 2 && a.Height == 2); b.Width == 3 && b.Height == 5); }
101
SwappingRectangles.Program.Swap (非正確版本)
static void Swap(Rectangle a, Rectangle b) { Rectangle temp = a; a = b; b = temp; }
102
SwappingRectangles 非正確版本之 記憶配置與參數傳遞
真實參數物件b 真實參數物件a 形式參數物件 a 區域變數 temp 呼叫函式Main記憶區 被呼叫函式Swap記憶區 區域變數 形式參數物件 b a.width a.height b.width b.height .
103
SwappingRectangles.Program.Main (正確版本)
static void Main(string[] args) { Rectangle a = new Rectangle(3, 5); Rectangle b = new Rectangle(2); Swap(ref a, ref b); Debug.Assert( a.Width == 2 && a.Height == 2); b.Width == 3 && b.Height == 5); }
104
SwappingRectangles.Program.Swap (正確版本)
static void Swap(ref Rectangle a, ref Rectangle b) { Rectangle temp = a; a = b; b = temp; }
105
SwappingRectangles 正確版本之 記憶配置與參數傳遞
區域變數 a.width 呼叫函式Main記憶區 a.height 真實參數物件a . . . 真實參數物件b b.width b.height . . . 形式參數物件 a 形式參數物件 b 區域變數 temp 被呼叫函式Swap記憶區
106
綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列
107
RotatingRectangles6.Program.Main片段
Rectangle[] rectangle = new Rectangle[3]; rectangle[0] = new Rectangle(3, 5); rectangle[1] = new Rectangle(2); rectangle[2] = new Rectangle(4, 1); rectangle[0].Rotate(); rectangle[1].Rotate(); rectangle[2].Rotate(); Debug.Assert(rectangle[0].Width == 5 && rectangle[0].Height == 3); Debug.Assert(rectangle[1].Width == 2 && rectangle[1].Height == 2); Debug.Assert(rectangle[2].Width == 1 && rectangle[2].Height == 4);
108
一維陣列rectangle宣告時的 記憶配置
109
一維陣列rectangle設值後的 記憶配置
堆積記憶區 堆疊記憶區 堆積記憶區 3 5 . rectangle[0] rectangle[1] rectangle[2] this . . rectangle this 2 this 2 . . . 4 1 . . .
110
唐詩五言絕句資料庫 物件陣列常常用來構建資料庫 以唐詩三百首中29首五言絕句的前23首建一個資料庫
方便使用者以詩題或詩人姓名查詢(query)全詩 一首詩(poem)看成一個物件,成員變數有詩題(title)、作者(author)、詩句(verse) 資料庫也看成一個物件,成員變數為詩物件的陣列,public成員函式為資料庫運作(process)
111
唐詩五言絕句資料庫 資料庫private成員函式:顯示標頭、取得使用者要求、以詩題查詢、以詩人姓名查詢
為常見的物件為基礎(object-based)的程式
112
PoemDataBase.Program.Main片段
Poem[] poem = new Poem[23]; poem[0] = new Poem("鹿柴", "王維", "空山不見人, 但聞人語響; 返景入森林, 復照青台上"); poem[1] = new Poem("竹里館", "王維", "獨坐幽篁裏, 彈琴復長嘯; 深林人不知, 明月來相照"); poem[2] = new Poem("送別", "王維", "山中相送罷, 日暮掩柴扉; 春草明年綠, 王孫歸不歸"); poem[3] = new Poem("相思", "王維", "紅豆生南國, 春來發幾枝; 願君多采擷, 此物最相思"); DataBase db = new DataBase(poem); db.Process();
113
PoemDataBase.DataBase片段 (1/5)
private Poem[] poem; public DataBase(){ poem = null; } public DataBase(Poem[] poem) { this.poem = poem;
114
PoemDataBase.DataBase片段 (2/5)
public void Process() { ShowHeading(); while (true) { int request = GetRequest(); if (request == 0) break; if (request == 1) ProcessQueryByTitle(); if (request == 2) ProcessQueryByAuthor(); } private void ShowHeading() { Console.WriteLine( "******唐詩三百首五言絕句資料庫******");
115
PoemDataBase.DataBase片段 (3/5)
private int GetRequest() { Console.WriteLine("選擇"); Console.WriteLine("0. 結束"); Console.WriteLine("1. 以詩題查詢"); Console.WriteLine("2. 以詩人姓名查詢"); int request = int.Parse( Console.ReadLine()); return request; }
116
PoemDataBase.DataBase片段 (4/5)
private void ProcessQueryByTitle() { Console.Write("輸入詩題: "); string title = Console.ReadLine(); for (int i = 0; i < poem.Length; ++i) { if (poem[i].Title == title) { Console.WriteLine( poem[i].Title + "\t" +poem[i].Author); Console.WriteLine(poem[i].Verse); } Console.WriteLine();
117
PoemDataBase.DataBase片段 (5/5)
private void ProcessQueryByAuthor() { Console.Write("輸入詩人姓名: "); string poet = Console.ReadLine(); for (int i = 0; i < poem.Length; ++i) { if (poem[i].Author == poet) { Console.WriteLine( poem[i].Title + "\t" +poem[i].Author); Console.WriteLine(poem[i].Verse); } Console.WriteLine();
118
PoemDataBase.Poem片段 (1/2)
private string title; private string author; private string verse; public Poem() { title = ""; author = ""; verse = ""; } public Poem(string title, string author, string verse) { this.title = title; this.author = author; this.verse = verse;
119
PoemDataBase.Poem片段 (2/2)
public string Title { get { return title; } } public string Author { get { return author; } public string Verse { get { return verse; }
Similar presentations