鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所

Slides:



Advertisements
Similar presentations
第一單元 建立java 程式.
Advertisements

鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
計算機程式語言實習課.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
四資二甲 第三週作業 物件導向程式設計.
第7章 C#函數與.NET Framework類別函數庫
第八章 类和对象.
Project 2 JMVC code tracing
Chapter 5 遞迴 資料結構導論 - C語言實作.
題目:十六對一多工器 姓名:李國豪 學號:B
Chapter 5 迴圈.
第5章 面向对象程序设计 本章要点 5.1 面向对象程序设计概述 5.2 Java语言的面向对象程序设计 5.3 方法的使用和对象数组
程式設計概論 1.1 程式設計概論 程式語言的演進 物件導向程式 程式開發流程 1.2 C++開發工具
C#程序设计 c# programming 泛型 C#程序设计课程组.
LINQ 建國科技大學 資管系 饒瑞佶.
第二章 C# 基础知识.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
Classes Lecturer: 曾學文.
第三章 C#面向对象初级编程 面向对象得程序设计越来越受到编程人员的喜爱。类和对象是面向对象程序设计中的重要概念。封装性、继承性和多态性是面向对象的特点,本章旨在全面说明C#编写面向对象程序设计的方法。
Ch10 類別與物件-方法 Java程式設計(2).
C#程序设计基础 $3 成员、变量和常量.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
類別(class) 類別class與物件object.
SQL Stored Procedure SQL 預存程序.
剪貼簿.
第三章 流程控制與例外處理 資訊教育研究室 製作 注意:本投影片僅供上課使用,非經同意,請勿散播或轉載。
Methods 靜宜大學資工系 蔡奇偉副教授 ©2011.
程式設計(二) 進階類別 郭文真 Sabrina Kuo.
C#面向对象程序设计 $7 继承和多态性.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
3.1 数据类型 3.2 标识符与关键字 3.3 常量 3.4 变量 3.5 运算符与表达式 3.6 一个编程实例
Java 程式設計 講師:FrankLin.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
JAVA 程式設計與資料結構 第四章 陣列、字串與數學物件.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
程式設計實習課(四) ----C 函數運用----
第六章 属性、索引器、委托和事件.
類別與物件 I (Classes and Objects I)
第一單元 建立java 程式.
標籤、按鈕、工具列、狀態列 (Labels, Buttons, Tool Strips, and Status Strips)
第三章 C# 基础知识.
Ch20. 計算器 (Mac 版本).
第 19 章 XML記憶體執行模式.
|07 函數.
C#程序设计基础 $3 成员、变量和常量.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
挑戰C++程式語言 ──第8章 進一步談字元與字串
Class & Object 靜宜大學資工系 蔡奇偉副教授 ©2011.
MicroSim pspice.
流程控制:Switch-Case 94學年度第一學期‧資訊教育 東海大學物理系.
OOP9 類別Class.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
陣列與結構.
方法進階及物件導向基礎 Lecturer: 楊昌樺.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
實習八 函式指標.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
第四章 陣列、指標與參考 4-1 物件陣列 4-2 使用物件指標 4-3 this指標 4-4 new 與 delete
JAVA 程式設計與資料結構 第三章 物件的設計.
What is “this”? 在物件導向程式設計中,類別的定義就是在說明如果創建了“這個物件”的話,它會具有那些屬性與功能,以及這些功能是如何實現的。 而所謂的“這個物件”就以 this 來表示。 當我們在JavaScript與jQuery中寫 script 程式(函式)時,“誰”呼叫這個函式,這個“誰”就是該函式中所謂的.
多國語系 建國科技大學 資管系 饒瑞佶.
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
String類別 在C語言中提供兩種支援字串的方式 可以使用傳統以null結尾的字元陣列 使用string類別
SQLite資料庫 靜宜大學資管系 楊子青.
Chapter 4 Multi-Threads (多執行緒).
C語言程式設計 老師:謝孟諺 助教:楊斯竣.
Unix指令4-文字編輯與程式撰寫.
方法(Method) 函數.
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
InputStreamReader Console Scanner
Presentation transcript:

鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所 物件與類別 鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所

綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列

綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列

三個矩形物件 b 2 a 5 2 c 1 3 4

旋轉90o 1 3 a c 4 5 b 2 2

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);

RotatingRectangles0.Program.Rotate static void Rotate(ref int width, ref int height) { int temp = width; width = height; height = temp; }

討論 矩形a的寬與高,分開宣告為width_a與height_a 旋轉90o應該是矩形物件具有的功能,在程式中卻完全看不出來 靠註解才能知道它們屬於同一個矩形 旋轉90o應該是矩形物件具有的功能,在程式中卻完全看不出來 單看程式碼很難體會它代表將矩形旋轉90o 使用幾乎沒有限制 即使兩個參數並不代表某個矩形物件的寬與高,任何程式師還是可以透過呼叫函式Rotate,來交換任兩個整數變數的數值

矩形物件列表 狀態 功能 名稱 寬 高 功能1 a 3 5 旋轉90o b 2 c 4 1

旋轉90o後矩形物件列表 狀態 功能 名稱 寬 高 功能1 a 5 3 旋轉90o b 2 c 1 4

矩形物件與類別

C# Class Rectangle class Rectangle { public int width; public int height; public void Rotate(){ int temp = width; width = height; height = temp; }

物件、類別 物件(Object) 類別(Class) 狀態與功能:例如, 矩形a, 電視機甲 藍圖:例如, 矩形類別, 泛稱電視機 狀態:成員變數(member variables) 如width, height 功能:成員函式(member function) 如Rotate

類別Rectangle之成員函式Rotate 功能為交換封裝一起的成員變數width和height之值 不需要由外界輸入參數 不用加上關鍵字static

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();

新增類別程式 專案>加入類別>類別 輸入類別名稱.cs

RotatingRectangles.Rectangle 框架程式碼 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace RotatingRectangles { class Rectangle }

封裝(Encapsulation) 類別將狀態變數與功能函式封裝起來 函式導向的程式也可將函式當作黑箱模組 使類別內容的修改更容易 若能把類別變成黑箱(black box)模組,使用的程式師不用了解成員變數與成員函式的細節,就能應用類別的功能 程式碼重複使用(code reuse) 函式導向的程式也可將函式當作黑箱模組 相關的變數沒有結合在一起 程式碼重複使用不是那麼方便

堆疊(Stack)與堆積(Heap) Heap . Stack

實值型別儲存方式 堆疊(Stack) int x = 100; x 100

參考型別儲存方式 堆積(Heap) 堆疊(Stack) string x = “abc”; x 參考 ‘a’ ‘b’ ‘c’

矩形物件記憶配置 堆積記憶區 堆疊記憶區 a b this a.width a.height b.width b.height 函式Rectangle.Rotate進入點 a.height b.height 堆疊記憶區 堆積記憶區

成員函式 同類別的物件,同一個成員函式只有一套函式程式碼 不同物件呼叫自己的成員函式,使用物件自己的成員變數 各物件就只存放這個程式碼進入點的位置 函式程式碼通常佔據相當大的記憶,如果每個物件都存放一套函式程式碼,需要很多記憶體 不同物件呼叫自己的成員函式,使用物件自己的成員變數 會把物件在堆積記憶區的地址(稱為this)也傳給成員函式 由this成員函式可知道如何取用對應的成員變數

private與public 一般電視機 類別 可以讓一般使用者調整的按鈕通常顯露在外 不讓使用者碰觸的電路裝置,都封裝在機殼內,對使用者來說無異黑箱 類別 相當於電視內部電路,作為自身工具幫手的成員變數與成員函式,宣告時加上關鍵字private,不讓外界程式師取用 像是電視的操控按鈕,提供給外界使用的成員變數與成員函式,則在宣告時加上關鍵字public 視為進一步的「封裝」

軟體IC 硬體IC 類別的封裝 內部的電路封裝在內,不輕易顯露 軟體工程師不需要了解private成員變數與成員函式 只要懂得public成員的使用,也能正確應用既有類別程式,達到減輕工作負荷,增進效率的目的

封裝 通常狀態變數為private ,只供同類別之功能函式取用,不可以由類別外直接取用 若有必要讓外界取得或設定其值,通常另外再寫public的成員函式來處理避開同名變數問題 避免被不慎改動,破壞狀態的正確一致性 容易維護,不影響外界程式 功能函式可能公用(public) ,也可能私有 一類別至少有一公用函式,以便應用

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;

RotatingRectangles2.Rectangle片段 (2/2) public int GetHeight() { return height; } public void Rotate() { int temp = width; width = height; height = temp;

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();

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);

偵錯器追踪 物件的成員變數值 「逐步執行」或按F11鍵,至進入類別 目前類別名稱、函式名稱 函式區域變數及參數 this及物件的成員變數值

綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列

設定或取得成員變數的值 通常將成員變數宣告為private,另外宣告public的成員函式來設定或取得成員變數的值,以維護成員變數間的一致性 類別中增加了很多只是要Set和Get成員變數值的簡單函式,使類別宣告有一點冗長,而呼叫這些函式時也得加上括弧,有一點累贅

屬性 (Attribute) 兼顧成員變數間的一致與程式寫法的簡潔 成員函式SetWidth和GetWidth可以整合為一個屬性Width,成員函式SetHeight和GetHeight可以整合為一個屬性Height 將屬性名稱取成與成員變數相同 唯一差別為開始字母之大小寫 屬性名稱也可以與成員變數不同 此處的作法可以省去找新名稱的麻煩,並使屬性與成員變數的關連清楚

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();

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);

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; }

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]);

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;

投擲兩顆骰子 非常可能發現兩顆骰子擲出的結果相同 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建立時,會使用相同的計算機時間做為亂數產生器的種子,使兩者得到的亂數數列完全相同

綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列

函式多載 (Overloading) 變數、函式、物件、類別都要選擇適切的名稱,程式才易寫易讀 FORTRAN語言函式庫的例子 適當的名稱常常不夠應用 FORTRAN語言函式庫的例子 讓計算實數參數與複數參數正弦函數的函式,雖然演算法不同,都命名為SIN 計算機自動依照參數型別的不同來決定該呼叫那一個函式 C++、Java、C#採相似的做法

分辨類別的成員函式 主要靠函式名稱,其次是參數的個數與型別 例:模擬時鐘運作 名稱相同,但是參數的個數或型別有所不同時,就可認為是不同的函式 時間的呈現可以是二十四小時制,也可以是區分上下午(AM、PM)的十二小時制 如果設定時間(SetTime)與取得時間(GetTime)的函式,對兩種不同時間呈現方式要取不同名稱,並不太容易,即使能夠,也會使函式名稱變得很長,不易使用

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");

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;

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;

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"; }

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;

綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列

初始值設定 建立物件後,必需以屬性或成員函式指定成員變數的值 如果忘了,就發生錯誤 最好在建立物件時便能設定必要成員變數的初值 沒有設定成員變數初值的錯誤,通常稱為「初始值未設定」錯誤(initialization error) 最好在建立物件時便能設定必要成員變數的初值

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);

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;

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;

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]++; }

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() {}

解構式 (Destructor) ~Dice() {} 與建構式相對,用於物件將被消滅的時候 會將物件佔用的堆積記憶區使用權歸還給系統,以便系統可以把這些記憶體分配給其他物件使用 稱為停用記憶區收集(garbage collection) 把不再使用的資源釋出 例如,某個處理檔案的物件,在建構式中開啟檔案,開始應用,當物件要被消滅時,便需在解構式中關閉檔案,以便他處可以再使用同一檔案

記憶漏失 (Memory Leakage) 記憶區回收的工作可能相當複雜,處理不好,就容易有忘記回收的區域 忘記回收的區域逐漸累積,就可能使程式繼續執行所需的記憶體不足,稱為記憶漏失 先前的程式語言如C++,經驗不足的程式設計師很容易就寫出有記憶漏失的程式碼 較新的語言如Java、C#通常讓系統自動回收記憶區,因此解構式內就不必再寫回收記憶區的程式敘述

解構式 可看成特別的成員函式 一個類別只能有一個解構式 必須能被外界呼叫,所以不可加上public或private等修飾語 沒有回傳值,所以也不宣告回傳值型別

物件產生與消滅流程 static void main(string[] arg) { Dice dice1 = new Dice(SEED_1); public Dice(int seed) rand = new Random(seed); } ~Dice(){} 宣告物件 建立物件 消滅物件dice1 Random建構式 }

綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列

物件宣告 在堆疊記憶區指定一個位址 接著以建構式建立物件 在堆積記憶區配置成員變數 填入成員變數初值 將堆積記憶區開始的位址放入堆疊記憶區的對應位置,成為物件的參考

this參考 堆疊記憶區有一個參考指到堆積記憶區 成員函式呼叫時會將參考this一併傳出 以便成員函式可以使用對應物件的成員變數 或用同樣方式呼叫其他成員函式

Rectangle物件記憶配置 堆積記憶區 堆疊記憶區 a b this a.width a.height b.width this b.height 函式Rectangle.Rotate進入點

參數名稱 在成員函式或建構式中可以特別用this指出物件本身的成員變數 參數名稱便可以取成與成員變數名稱相同

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; }

要當心的情況 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);

綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列

不需物件的函式呼叫 例如兩個與時間轉換相關的函式HoursToMins和DaysToHours 可以寫成主程式裏的函式,供主程式呼叫 分別把小時數換算成分鐘、天數換算成小時 HoursToMins(5)為300(5小時等於300分鐘) DaysToHours(4)為96(4天等於96小時) 可以寫成主程式裏的函式,供主程式呼叫 這兩個函式就被綁在主程式裏,不容易給其他的類別程式使用

靜態成員函式 把函式連同它們要用到的常數,一起包在一個類別內 宣告時加註關鍵字static,表示是靜態成員函式,不會用到物件的成員變數 類別中宣告的常數不算成員變數 呼叫時,不需先建立物件,直接以類別名稱呼叫即可

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;

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);

靜態成員函式 主程式Main的執行,並不需要用到Program物件的成員變數 數學函數,如Exp、Log、Sin、Cos等,也都是系統類別Math的靜態成員函式 要以類別名稱Math呼叫

計算物件產生的個數 可以在主程式中放一個計數器,每建立一個物件就累加一次 如果主程式外的其他類別成員函式執行時,也會產生物件,主程式中的計數器就算不到這些另外建立的物件 物件計數器最好還是放在類別中 讓類別自己統計已建立的物件總數

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); }

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;

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; }

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進入點

綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列

UsingStruct.Point2D struct Point2D { public int x; public int y; }

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); }

類別與結構的記憶配置 由類別產生的物件放一個參考在堆疊區 由結構建立的物件把所有的成員變數與成員函式進入點都存放在堆疊區 指到堆積記憶區的成員變數的開始位置 由結構建立的物件把所有的成員變數與成員函式進入點都存放在堆疊區

StructVSClass.SPoint2D struct SPoint2D { public int x; public int y; public SPoint2D(int x, int y) { this.x = x; this.y = y; }

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;

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);

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);

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);

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);

結構物件記憶配置 堆疊記憶區 this this this sPt1.x sPt1.y sPt2.x sPt2.y sPt3.x 建構式SPoint2D進入點

類別物件記憶配置 堆疊記憶區 堆積記憶區 this cPt1.x cPt1 cPt1.y this cPt2.x cPt2.y cPt2 預設建構式CPoint2D進入點 建構式CPoint2D進入點

練習 宣告並測試結構Student,其中包括學號、姓名、成績

綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列

類別物件設值 僅將堆疊記憶區的物件參考設成相同 不改變成員變數之值 使原先不同的物件綁在一起,成為相同物件,容易產生語義錯誤 若程式有造成這種語義錯誤的可能,應該在設值時使用複製建構式(copy constructor)

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);

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);

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;

UsingCopyConstructor執行後之 記憶配置 堆積記憶區 堆疊記憶區 cPt1 this cPt1.x cPt1.y cPt2 this cPt3 this 建構式CPoint2D進入點 複製建構式CPoint2D進入點 cPt1.x cPt1.y cPt1.x cPt1.y

淺層複製與深層複製 淺層複製(Shallow copy) (設值敘述) 深層複製(Deep copy) (複製建構式) 系統提供 只複製參考(Reference)地址 沒有新物件產生 深層複製(Deep copy) (複製建構式) 程式師提供 產生新物件 應複製所有資料成員

練習 宣告並測試類別Student,其中包括學號、姓名、成績,仿照程式StructVSClass及UsingCopyConstructor分別使用設值與複製建構函式產生Student物件,觀察淺層複製與深層複製的差別

綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列

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); }

SwappingRectangles.Program.Swap (非正確版本) static void Swap(Rectangle a, Rectangle b) { Rectangle temp = a; a = b; b = temp; }

SwappingRectangles 非正確版本之 記憶配置與參數傳遞 真實參數物件b 真實參數物件a 形式參數物件 a 區域變數 temp 呼叫函式Main記憶區 被呼叫函式Swap記憶區 區域變數 形式參數物件 b a.width a.height b.width b.height .

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); }

SwappingRectangles.Program.Swap (正確版本) static void Swap(ref Rectangle a, ref Rectangle b) { Rectangle temp = a; a = b; b = temp; }

SwappingRectangles 正確版本之 記憶配置與參數傳遞 區域變數 a.width 呼叫函式Main記憶區 a.height 真實參數物件a . . . 真實參數物件b b.width b.height . . . 形式參數物件 a 形式參數物件 b 區域變數 temp 被呼叫函式Swap記憶區

綱要 由函式到類別 屬性 函式成員之多載 建構式與解構式 this 參考 靜態成員 結構 複製建構式 物件作為函式參數 物件陣列

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);

一維陣列rectangle宣告時的 記憶配置

一維陣列rectangle設值後的 記憶配置 堆積記憶區 堆疊記憶區 堆積記憶區 3 5 . rectangle[0] rectangle[1] rectangle[2] this . . rectangle this 2 this 2 . . . 4 1 . . .

唐詩五言絕句資料庫 物件陣列常常用來構建資料庫 以唐詩三百首中29首五言絕句的前23首建一個資料庫 方便使用者以詩題或詩人姓名查詢(query)全詩 一首詩(poem)看成一個物件,成員變數有詩題(title)、作者(author)、詩句(verse) 資料庫也看成一個物件,成員變數為詩物件的陣列,public成員函式為資料庫運作(process)

唐詩五言絕句資料庫 資料庫private成員函式:顯示標頭、取得使用者要求、以詩題查詢、以詩人姓名查詢 為常見的物件為基礎(object-based)的程式

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();

PoemDataBase.DataBase片段 (1/5) private Poem[] poem; public DataBase(){ poem = null; } public DataBase(Poem[] poem) { this.poem = poem;

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( "******唐詩三百首五言絕句資料庫******");

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; }

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();

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();

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;

PoemDataBase.Poem片段 (2/2) public string Title { get { return title; } } public string Author { get { return author; } public string Verse { get { return verse; }