1.3k likes | 1.36k Views
第十章. 定義類別和物件實體. 本章學習目標:. 宣告類別後,類別內必須包含資料成員(屬性)和成員函數(方法)。 類別和存取權限的關係,並將封裝的概念轉化為實作內容。 將物件初始化的建構式,及建構式的種類。 類別也有成員,包含了靜態屬性和靜態成員函數。 使用 this 指標來指向物件本身。. 類別為何是物件原型,物件為何是類別的實體化?本將利用實作方式來說明:. 10-4-1 預設建構式 10-4-2 複製建構式 10-4-3 型別轉換建構式 10-4-4 組合物件建構式. 10-4 建構式的種類. 10-1-1 產生類別
E N D
第十章 定義類別和物件實體
本章學習目標: • 宣告類別後,類別內必須包含資料成員(屬性)和成員函數(方法)。 • 類別和存取權限的關係,並將封裝的概念轉化為實作內容。 • 將物件初始化的建構式,及建構式的種類。 • 類別也有成員,包含了靜態屬性和靜態成員函數。 • 使用this指標來指向物件本身。 類別為何是物件原型,物件為何是類別的實體化?本將利用實作方式來說明:
10-4-1 預設建構式 10-4-2 複製建構式 10-4-3 型別轉換建構式 10-4-4 組合物件建構式 10-4 建構式的種類 10-1-1 產生類別 10-1-2 宣告資料成員 10-1-3 宣告成員函式 10-1-4 使用範圍解析運算子 10-1-5 建立物件實體 10-1 建立類別和物件 10-5-1 認識類別成員 10-5-2 靜態屬性 10-5-3 靜態成員函數 10-5 類別成員 10-2-1 存取權限 10-2-2 資料保護 10-2 資料封裝 10-6-1 物件指標 10-6-2 動態配置指標 10-6-3 this指標的用途 10-6 物件與指標 10-3-1 物件的初始化 10-3-2 清除物件 10-3-3 物件的生命週期 10-3 使用建構式 章節目錄
10-4-1 預設建構式 10-4-2 複製建構式 10-4-3 型別轉換建構式 10-4-4 組合物件建構式 10-4 建構式的種類 10-5-1 認識類別成員 10-5-2 靜態屬性 10-5-3 靜態成員函數 10-5 類別成員 10-2-1 存取權限 10-2-2 資料保護 10-2 資料封裝 10-6-1 物件指標 10-6-2 動態配置指標 10-6-3 this指標的用途 10-6 物件與指標 10-3-1 物件的初始化 10-3-2 清除物件 10-3-3 物件的生命週期 10-3 使用建構式 章節目錄
10-1 建立類別和物件 10-1-1 產生類別 10-1-2 宣告資料成員 10-1-4 使用範圍解析運算子 10-1-3 宣告成員函式 10-1-5 建立物件實體 章節目錄
10-1-1 產生類別 10-1-2 宣告資料成員 10-1-4 使用範圍解析運算子 10-1-3 宣告成員函式 10-1-5 建立物件實體 章節目錄 10-1 建立類別和物件
10-1 建立類別和物件 • 對於物件導向的觀念有所認識之後,我們要以C++程式語言的觀點來深入探討類別和物件的實作,透過物件導向程式設計(OOP)的運作,更進一步瞭解!
10-1-1 產生類別 10-1-2 宣告資料成員 10-1-4 使用範圍解析運算子 10-1-3 宣告成員函式 10-1-5 建立物件實體 章節目錄 10-1 建立類別和物件
10-1-1 產生類別 • 我們已經知道「類別」(class)是物件的原型,在使用物件之前先熟悉類別的建立。語法如下:
為關鍵字,用來定義類別 class • 建立類別使用的名稱,同樣地也必須遵守識別字的命名規範 • 此外,類別名稱只是用來定義類別,必須等到類別實體化之後才會取得記憶體空間的配置 類別名稱 private public protected • 關鍵字,用來設定資料成員的存取權限 資料成員 (Data Member) 成員函式 (Member Function) • 資料成員負責儲存資料的變數 • 定義類別時,存取權限必須存放在區塊{}內,結束時必須加上分號「;」 類別(class)語法解說:
類別的敘述表示 • 瞭解類別的宣告後,如果要利用程式碼來撰寫一個類別,寫法如下: 此處我們宣告一個car類別
10-1-1 產生類別 10-1-2 宣告資料成員 10-1-4 使用範圍解析運算子 10-1-3 宣告成員函式 10-1-5 建立物件實體 章節目錄 10-1 建立類別和物件
10-1-2 宣告資料成員 • 完成類別的宣告後,得定義資料成員(屬性),語法如下: • 其實這和第二章所介紹的宣告變數方法相同,只是此處宣告的對象是資料成員名稱!若要建立一個汽車的類別,敘述如下: 我們在car類別加入二個資料成員,它們的存取權限是public,表示這些資料成員皆能自由存取。
10-1-1 產生類別 10-1-2 宣告資料成員 10-1-4 使用範圍解析運算子 10-1-3 宣告成員函式 10-1-5 建立物件實體 章節目錄 10-1 建立類別和物件
10-1-3 宣告成員函式 • 在物件導向程式語言中,除了要有屬性(資料成員),還得加上方法(成員函式) • 因此還必須在類別內宣告成員函式,語法如下: • 其實大家已經發現,這和第六章介紹的函數相同,要有回傳資料型別、函式名稱和參數串列。 • 如果要在car類別內加入成員函式,程式碼如下:
10-1-1 產生類別 10-1-2 宣告資料成員 10-1-4 使用範圍解析運算子 10-1-3 宣告成員函式 10-1-5 建立物件實體 章節目錄 10-1 建立類別和物件
10-1-4 使用範圍解析運算子 • 當我們在類別裡定義了成員函式的原型後,倘若要在類別定義範圍外使用成員函式時,必須利用範圍解析運算子「::」指定成員函數所隸屬的類別。語法如下: • 延續car類別範例,如果要在car類別之外使用setSpeed()成員函數,寫法如下:
10-1-1 產生類別 10-1-2 宣告資料成員 10-1-4 使用範圍解析運算子 10-1-3 宣告成員函式 10-1-5 建立物件實體 章節目錄 10-1 建立類別和物件
10-1-5 建立實體物件 • 宣告類別後,若要將類別實體化,就得建立物件 • 不過,對於初學者來說,OOP的類別和物件非常容易混淆,如果我們把類別視為「自訂的資料型別」,物件就是類別中所宣告的變數! • 如此一來,只要定義類別,並將物件當作基本資料型別的變數來處理就可以了。 • 如何建立一個物件,語法如下:
物件的敘述 • 倘若要建立一個car類別的物件,敘述如下: 從OOP的觀點來看,表示將car類別實體化,建立一個march物件。
成員的存取 • 產生物件後 • 物件的狀態如何被改變? • 如何利用方法來進行操作? • 必須利用「.」運算子來存取類別中所產生物件的成員,語法如下: 利用範例「car.vcproj」來說明類別的宣告和物件實體的建立。
執行程式:car.vcproj • 利用結構的觀念來定義:學生成績和儲存輸入資料。 • 第10~14行定義學生成績的結構,包含以字元陣列來處理的學生姓名、儲存學生成績和成績等級三種結構欄位。 • 第17~19行的SaveData結構用來諸存輸入的資料,最大筆數是20筆。 • 第25~42行為主程式,利用第28~41行while迴圈來判斷使用者是否要新增資料。而31~40行利用switch來進行條件選擇。使用者按下A表示要新增資料,按下E表示結束新增的動作。
範例「car.vcproj」的思考 • 範例「car.vcproj」說明兩件事: • car類別可依據實際需求來產生不同狀態的物件。 • 每個物件的狀態是獨立的,當march物件的狀態改變時並不會影響marchwin物件。
章節目錄 10-2 封裝資料
10-2 資料封裝 • 封裝的作用是讓使用者利用介面來簡化操作程序 • 大家可以想像一下!如果搭乘的電梯缺少控制面板是什麼情形? • 使用者可能得自己計算電動機的電流,才能讓曳引機帶動到欲前往的樓層距離。 • 但是若以OOP的觀點來看,只要將控制面板上的移動方法設定好,能與電梯物件的某個屬性對應,搭乘電梯就非常簡便。 • 一般而言,將封裝的概念應用到程式設計上,就是類別內的資料成員只能透過類別內的成員函數來存取。
10-2-1 存取權限 章節目錄 10-2 封裝資料
10-2-1 存取權限 • 在類別內如何以成員函數來存取資料成員!當我們宣告類別時,共有三個不同等級的存取權限: • public、private和protected。 • 三者的作用以下表10-1表示:
private • 表示任何類別皆可存取,適用於對外公開的資料 • 當物件的資料不想對外公開時,只能被類別內的成員函數來存取,或是同類別的其他物件也能存取該物件的資料 public protected • 只有該類別或繼承這類別的子類別的物件(會在第11章繼續討論) 存取權限的範圍
利用方法封裝物件 • 我們在第九章討論過「封裝」概念(參考9-2-1資料抽象化)!在物件導向技術的世界裡,為了達到「資訊隱藏」目的,可以透過「方法」來封裝物件的屬性。 • 存取權限的作用能讓物件掌握成員,控制物件在被允許的情形下才能讓外界使用。 • 為了保護物件的屬性不被外界其他類別所存取,通常會將資料成員宣告為private; • 再利用成員函數做為物件和其他物件的介面,因此常宣告為public。 • 有了這樣的概念,我們再回頭來看看第一個範例「car.cpp」,並將資料成員的存取權限要更改為private,再透過成員函數來存取這些資料成員,如範例upcar.vcproj所示。
巢狀結構的存取 • 若要存取學生成績中的姓名,其敘述如下: • 這表示要先透過處理資料的stu_data結構變數,才能進一步存取。利用SaveData結構中宣告的db_rds欄位(為student結構變數)進行學生成績結構中的姓名欄位存取。
執行程式:upcar.vcproj 此範例說明「封裝」的作用,將UpCar類別的color、engi屬性設為private,然後利用類別本身的成員函數setColor()來存取color屬性,setEngi()來存取engi屬性。從另一個觀點來看,透過相對應的「方法」才能改變物件的「屬性」。
程式解說:upcar.vcproj • 第7~16行定義UpCar類別及相關的資料成員和成員函數。 • 第24~35行定義setColor()成員函數,當使用者選擇color屬性時,執行顏色的設定。此處利用switch/case敘述來接受使用者選擇的顏色。 • 第38~43行定義setEngi()成員函數,當使用者選擇engi屬性時,執行排氣量的設定。利用if進行條件判斷來顯示排氣量。 • 第46~63行是主程式,第50行先讓使用者選擇車款,然後再選擇顏色和排氣量。這些屬性設定會改變march和marchwin物件的狀態。
10-2-1 存取權限 10-2-2 資料保護 章節目錄 10-2 封裝資料
10-2-2 資料保護 • 還記得const這個關鍵字,在第2章曾討論過:當資料的值不想被改變時,可利用const將資料的值宣告為「常數」。 • 若從資料保護的觀點來看 • 存取控制只是讓資料存取有所依據 • 想要更嚴謹的保護資料,可透過const將類別中的成員函數、物件再加上一道保護鎖!
常數函數 • 所謂常數函數是一個「唯讀函數」(Read-only function),表示函數在執行時無法改變物件的狀態。語法如下: 如何讓成員函數形成常數函數,就是在參數串列之後,進入區塊之前,加入const關鍵字。透過範例「ConstFunc.vcproj」說明成員函數變成常數函數執行時產生錯誤的情形!
執行程式:ConstFunc.vcproj(錯誤) • 第6~11行宣告class類別,其中第8行成員函數setHeight()因為加上const關鍵字形成常數函數,第10行height為資料成員。 • 第17~19行定義getHeight()成員函數,將取得的資料成員height利用return敘述回傳。 • 第21~25行是主程式,第22行宣告一個Hobbit物件,第23行呼叫setHeight()成員函數進行參數傳遞,第24行利用getHeight()成員函數來輸出結果。
常數函式的特性 • 第18行為什麼會產生錯誤?這是一個很典型的OOP程式 • 將height屬性的存取權限設為private • 利用setHeight()成員函數來修改屬性 • 再透過getHeight()成員函數回傳物件改變後的狀態 • 問題就出自於setHeight()成員函數已經被宣告為常數函數,表示它不能修改物件的屬性,因此const很明顯放錯位置,我們將程式碼修正。
執行程式:ConstFuncMd.cpp • 利用setHeight()成員函數來修改屬性,而以getHeight()成員函數回傳改變後的物件狀態,所以將getHeight()成員函數宣告為常數函數,確保回傳過程中屬性值不會再被改變。 • 所以將第7行的函式原型和第13~15行定義getHeight()成員函數加上const關鍵字,而把setHeight()成員函數原有的const關鍵字刪除即可讓程式輸出正確結果。
常數物件 • 當物件被宣告為常數物件時,表示任何成員函數都無法改變物件的屬性。 • 此時,常數物件呼叫成員函數時,只能呼叫常數函數來確保物件的屬性不會被改變。 • 我們延續上一個範例來說明常數物件。
執行程式:ConstFuncMd.cpp 顯示錯誤訊息:ConstFuncMd.cpp(25) : error C2662: 'people::setHeight' : 無法將'this' 指標從'const people' 轉換成'people &'轉換遺失限定詞 • 範例中原有的內容並沒有改變,在主程式中第22行宣告flores為常數物件,然後在第25、26行分別呼叫了setHeight()和getHeight()二個成員函數 • 因為flores是常數物件,所以只能呼叫常數函數getHeight() • 當常數物件呼叫了setHeight()成員函數時會顯示錯誤訊息