C++ 與 物件導向 程式設計概念簡介 魏天君 d908301@oz.nthu.edu.tw 2018/11/24
何謂物件?何謂物件導向? 物件 (Object): 使用者可以自訂資料型態,將多個基本資料型態及函式加以封裝,形成單一的物件,用在處理複雜的應用需求時更便利、更具組織性 物件導向 (Object Oriented): 不再是單純地結構化程式設計,而是以物件為基礎來考量並規畫程式架構 2018/11/24
定義類別 (只包含 data member) class CAccount { public: int m_iAccNum; string m_sName; double m_dMoney; }; 2018/11/24
利用類別產生物件 CAccount accMerry; accMerry.m_iAccNum = 2058; accMerry.m_sName = "Merry"; accMerry.m_dMoney = 350.0; 2018/11/24
利用全域函式處理物件 呼叫函式時 Deposit(accMerry, 200.0); void Deposit(CAccount &accPerson, double dMoney) { accPerson.m_dMoney += dMoney; } 呼叫函式時 Deposit(accMerry, 200.0); cout << "Merry's money: " << accMerry.m_dMoney << endl; 2018/11/24
利用類別產生物件 CAccount *paccGeorge = new CAccount; (*paccGeorge).m_iAccNum = 2056; paccGeorge->m_sName = "George"; paccGeorge->m_dMoney = 100.0; 2018/11/24
定義類別 (包含 member function) class CAccount { public: int m_iAccNum; string m_sName; double m_dMoney; void Deposit(double dMoney) m_dMoney += dMoney; } }; 2018/11/24
使用 member function accMerry.Deposit(200.0); cout << "Merry's money: " << accMerry.m_dMoney << endl; 2018/11/24
this 指標 當類別的成員函式執行時,它自動含有一個隱藏的指標,稱為 this,這指標含有所屬物件的位址 例如若有敘述 accMerry.Deposit(); 則函式 Deposit() 中的 this 指標含有 accMerry 的位址 2018/11/24
放置 member function 定義之處 習慣上將 class 定義放在 *.h 中 member function 需在 class 定義中宣告 習慣上將 member function 定義放在 *.cpp 中 (若放在 class 定義中視為 inline function) 2018/11/24
CAccount.h #ifndef _CAccount_H #define _CAccount_H #include <string> using namespace std; class CAccount { public: int m_iAccNum; string m_sName; double m_dMoney; void Deposit(double dMoney); }; #endif 2018/11/24
CAccount.cpp #include "CAccount.h" void CAccount::Deposit(double dMoney) { m_dMoney += dMoney; } 2018/11/24
main.cpp #include <iostream> #include "CAccount.h" using namespace std; typedef CAccount ACT; // 可使用 typedef 來導入助憶同義字 int main() { ACT accMerry; /* 其他程式碼 */ return 0; } 2018/11/24
建構子 class CAccount { public: int m_iAccNum; string m_sName; double m_dMoney; CAccount() m_dMoney = 100.0; /* 其他程式碼 */ } void Deposit(double dMoney); }; 2018/11/24
放置建構子定義之處 建構子定義的放置處與一般成員函式同 若將建構子定義寫在 class 定義之外,則寫法如下: CAccount::CAccount() { m_dMoney = 100.0; } 2018/11/24
預設建構子 (default constructor) 當你定義一個類別時,若沒有提供自己的建構子,則編譯器會提供一個預設建構子 編譯器提供的預設建構子沒有參數,亦不會初始化所產生之物件的資料成員 一旦你加入自己的建構子,編譯器就不會自行提供任何建構子 2018/11/24
建構子的特徵 建構子的名稱必需與 class 名稱相同 建構子不可有回傳值,函式宣告前亦不可加 void 建構子可以有參數列 建構子可形成多載 2018/11/24
有參數列的建構子 CAccount(double dInitMoney) { m_dMoney = dInitMoney; } 需注意一旦你加入自己的建構子,編譯器就不會自行提供任何預設建構子 2018/11/24
多載的建構子 CAccount() { m_dMoney = 100.0; } CAccount(double dInitMoney) m_dMoney = dInitMoney; CAccount(string sName, double dInitMoney) m_sName = sName; 2018/11/24
使用建構子產生物件 CAccount accMerry; CAccount accMerry = CAccount(); CAccount accMerry(); // error,被視為函式宣告 CAccount accGeorge(250.0); CAccount accGeorge = CAccount(250.0); CAccount accAnn("Ann", 300.0); CAccount *paccPaul = new CAccount("Paul", 760.0); // 記得用動態配置的物件在結束使用時要釋放 2018/11/24
建構子中參數的預設值 CAccount(double dInitMoney = 100.0) { m_dMoney = dInitMoney; } 預設參數值應放在宣告中,而不是定義中 需注意參數預設值造成模棱兩可的情況 2018/11/24
建構子中使用初值串列 CAccount(string sName, double dInitMoney) : m_sName(sName), m_dMoney(dInitMoney) { /* 其他程式碼 */ } 上述程式碼與下述程式碼有相同的結果,但過程不同 m_sName = sName; m_dMoney = dInitMoney; 2018/11/24
類別的私有成員 class CAccount { public: int m_iAccNum; string m_sName; double m_dMoney; CAccount(); void Deposit(double dMoney); private: double m_dInterest; }; 2018/11/24
類別的私有成員 類別的私有成員僅可透過類別內部進行存取,例如我們有一個 public 的成員函式 CalcInterest( ) 它會將存戶的餘額加上利息 void CAccount::CalcInterest() { m_dMoney += m_dMoney * m_dInterest; } 2018/11/24
類別的私有成員 一般而言,我們喜歡將所有的成員資料皆宣告為 private,以符合資料隱蔽的原則 我們會提供一組存取 private 資料的 public 成員函式,使得我們可以在 class 設計者的監控下,安全地對 private 資料做存取、修改 習慣上,簡單的 private 資料存取函式會以 setXXX() 及 getXXX() 命名,並設定為 inline 函式,以減少效率上的衝擊 2018/11/24
類別的私有成員 請試著修改我們的 CAccount 類別,使其符合上頁說明的習慣 2018/11/24
類別的靜態成員 以關鍵字 static 修飾的 data member 或 member function 即為靜態成員 2018/11/24
類別的靜態資料成員 class CAccount { public: static int m_iSerialNo; int m_iAccNum; string m_sName; double m_dMoney; CAccount(); void Deposit(double dMoney); private: double m_dInterest; }; 2018/11/24
類別的靜態資料成員 類別的靜態資料成員需在類別之外撰寫初值化 int CAccount::m_iSerialNo = 0; 2018/11/24
類別的靜態資料成員 修改我們的 CAccount 類別之建構子,使其可以為每個新開的戶頭指派獨一無二的流水號帳號 2018/11/24
類別的靜態成員函式 靜態成員函式中只可存取靜態資料成員 非靜態成員函式亦可存取靜態資料成員 2018/11/24
關鍵字 explicit 對於只有單一參數的建構子函式,編譯器可用這種建構函式將參數型態暗中轉換為類別型態 在某些情況下,這會產生意想不到的結果,請直接看 CAccount 的 "explicit" 範例說明 可以在單一參數的建構子函式宣告前面加上 explicit 關鍵字,避免編譯器作暗中的轉換 2018/11/24
夥伴 (friend) 有時候我們需要將某些特定的函式視為類別的 "榮譽成員",允許它們存取類別的 private 資料,彷彿它們是類別的成員一般,這種函式稱為此類別的夥伴 (friend) 我們可以將整個類別指定為另一個類別的夥伴,或將某個函式指定為另一個類別的夥伴 請直接看 CAccount 的 "夥伴函式" 範例瞭解夥伴函式的使用方式 2018/11/24
類別物件的陣列 類別物件陣列的宣告與其他型態陣列的宣告完全一樣 類別物件陣列的每個元素都需個別產生,而編譯器會為每個元素呼叫預設建構函式,編譯器不讓我們在定義敘述中初值化陣列 請直接看 CAccount 的 "物件陣列" 範例 2018/11/24
類別物件的陣列 使用一般陣列 CAccount accGroup[10]; 動態產生陣列 CAccount *paccGroup = new CAccount[10]; 使用 vector vector<CAccount> vaccGroup(10); vaccGroup.push_back(CAccount()); 2018/11/24
類別物件的大小 可以用 sizeof 取得類別物件的大小 cout << sizeof(CAccount) << endl; cout << sizeof(accTom) << endl; 2018/11/24
在類別中使用其他類別物件 我們希望在我們的 CAccount 類別中包含一個 CPerson 的物件,用來記錄客戶資料 新增 CPerson.h、CPerson.cpp 檔來製作 CPerson 類別 在 CAccount.h 中 #include "CPerson.h" 並使用 CPerson 類別 請見 CAccount 的 "使用 CPerson 物件" 範例程式 2018/11/24
拷貝建構子 (copy constructor) 若不自訂拷貝建構子,則由編譯器提供預設拷貝建構子 假設有一敘述式 CPerson psnP1; 則接下來的兩個敘述式皆會喚起拷貝建構子 CPerson psnP2(psnP1); CPerson psnP3 = psnP1; 2018/11/24
拷貝建構子 (copy constructor) 預設的拷貝建構子將 data member 的值一對一拷貝,但當 data member 中包含指標時,此拷貝動作會產生不適當的結果 試著將我們 CPerson 類別的 sName 這個 data memeber 改用 C-style 字串來儲存,並利用預設的拷貝建構子來觀察其行為 2018/11/24
拷貝建構子 (copy constructor) 自訂拷貝建構子的函式宣告如下 CPerson(const CPerson &psnPerson); 請見 CPerson 的 "拷貝建構子" 範例 2018/11/24
解構子 (destructor) 解構子的宣告 ~CAccount(); 解構子的定義 CAccount::~CAccount() { /* 解構程式碼 */ } 沒有自訂解構子時,編譯器亦會提供一個預設解構子 2018/11/24
使用建構子初值串列的時機 初值串列直接以 constructor 對 data member 做初始化,而非以指派方式完成初始動作 當你的 data member 中含有大型 class 時,使用初值串列可提高執行效率 當你的 data member 沒有 default constructor 時,必需使用初值串列來喚起其 constructor 2018/11/24
const 物件和 const 成員函式 標明為 const 的物件,表示此物件不會被修改 const 物件只能呼叫 const 成員函式 void GetMoney() const; 關鍵字 const 也必需出現在函式定義中 void CAccount::GetMoney() const 2018/11/24
類別的 mutable 資料成員 有一些情況即使物件宣告為 const,但你仍然需要修改某些特定的資料成員,此時就必需將資料成員宣告為 mutable 2018/11/24
類別的 mutable 資料成員 class SecureAccess { public: bool isLocked() const; /* other class definition */ private: mutable int time; } bool SecureAccess::isLocked() const time = getCurrentTime(); return lockStatus(); 2018/11/24
類別物件的參考與執行效能 試思考函式呼叫時,採用以值傳遞與採用物件參考傳遞,對執行效能的影響 以值傳遞時需要拷貝物件 以參考傳遞時不需拷貝物件 2018/11/24
類別物件的指標與執行效能 假設我們有一個 CAccount 陣列,我們希望依存款餘額對此陣列做排序 試思考與比較如何撰寫程式碼會較有效率 2018/11/24
Case Study 定義一個交易記錄的類別 CTrans 試著在我們的 CAccount 類別中加入交易記錄這項 data member,並增加 "新增"、"刪除" 交易記錄的功能 請看 CAccount 的 "增刪交易記錄" 範例 2018/11/24