【C++ 筆記】資料抽象與 ADT - part 25 === 目錄(Table of Contents): [TOC] --- 很感謝你點進來這篇文章。 你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧! 簡介 --- > Data abstraction refers to providing only essential information about the data to the outside world, ignoring unnecessary details or implementation. > From GeeksForGeeks 資料抽象指的是只提供外界所必要的資訊,忽略不必要的細節或實作面。 什麼意思呢?就像你在玩遊戲時,可以看到角色的 HP、MP 等資訊,但你不需要知道這些數值是怎麼計算的、存在哪個記憶體位址、或者用什麼資料結構去記錄這些資訊。 遊戲開發者只會讓你看到角色的重要狀態資訊,而把它的具體表示方法(如 class 的欄位、加密編碼或計算過程)藏起來,這就是資料抽象。 ### 資料抽象的優點 1. 簡化: 前面有說到資料抽象可以隱藏內部實作細節,只提供必要的介面,讓程式設計易懂、易管理。user 不用去關心物件如何運作,只需透過公開的介面使用功能。 2. 程式碼的可維護性: 因為內部實作細節被封裝起來,修改類別的內部結構或演算法時,只要保持介面不變,外部 user 的程式碼不需改動,減少了維護成本和錯誤風險。 3. 保護資料完整性: 將資料成員設為 private,防止外部直接修改,避免因誤用導致物件狀態不一致或錯誤,提升程式的安全性和穩定性。 4. 可使程式碼重複使用: 資料抽象使得類別設計更具模組化,可以在不同專案或上下文中重複使用相同的抽象類別或介面,提高開發效率。 ### 標頭檔也有資料抽象 如 `<algorithm>` 裡面的 `sort()`,不需要知道他是用什麼排序法去寫的,你只要呼叫這個函式,參數填一填,他就可以用了。([菜鳥教程](https://www.runoob.com/cplusplus/cpp-data-abstraction.html)) 另外一個例子是 `<cmath>`,裡面的 `pow()` 函式,也不用知道中間演算的過程,反正呼叫這函式就可以用了。([GeeksForGeeks](https://www.geeksforgeeks.org/cpp/abstraction-in-cpp/)) ### 封裝與抽象 兩者概念很像,但實際上還是有差別。 抽象只是程式設計的設計概念,而封裝是實現抽象的技術手段(如用存取修飾子控制外部權限存取)。 資料抽象之實作方式 --- 當然就是使用 class 了,class 將資料(成員變數)和操作資料的函式(成員函式)封裝在一起,並透過存取修飾詞(public、private、protected)控制外部對資料的存取權限。 * public:公開介面,外部程式可以存取。 * private:私有成員,外部無法直接存取,隱藏實作細節。 * protected:受保護成員,僅限類別本身及其衍生類別存取。 在此之前,要再說明一個觀念是 ADT(Abstraction Data Type),資料抽象型態,在 C++ 中的類別都是屬於 ADT 的實作。 > An Abstract Data Type (ADT) is a conceptual model that defines a set of operations and behaviors for a data structure, without specifying how these operations are implemented or how data is organized in memory. > From GeeksForGeeks 資料抽象型態是一種概念模型,為資料結構定義了一組操作和行為,而不指定這些操作如何實現或資料如何在記憶體中組織。 以下有兩個例子,都同屬於 AD、ADT 的範疇: ```cpp= // account.cpp #include <iostream> using namespace std; class BankAccount { private: int balance; public: BankAccount() { balance = 0; } void deposit(int amount) { if (amount > 0) balance += amount; } void withdraw(int amount) { if (amount <= balance) balance -= amount; } int getBalance() { return balance; } }; int main() { BankAccount account; account.deposit(1000); account.withdraw(300); cout << "目前餘額:" << account.getBalance() << endl; return 0; } ``` ```cpp= // door.cpp #include <iostream> using namespace std; class Door { private: bool isOpen; public: Door() { isOpen = false; } void open() { isOpen = true; } void close() { isOpen = false; } bool status() { return isOpen; } }; int main() { Door d; d.open(); cout << "門的狀態:" << (d.status() ? "開啟" : "關閉") << endl; return 0; } ``` 首先資料抽象在哪: - account.cpp:user 可用 deposit()、withdraw()、getBalance() 成員函式,而不需要知道 balance 是如何運作的。 - door.cpp:user 可用 open()、close() 成員函式去控制門的開關,不用知道 isOpen 他是什麼 bool 變數,怎麼運作的等等。 上述的 balance、isOpen 都用到 private 存取修飾子控制權限,只有 class 內部可以使用,外部無法存取。 那 ADT 呢? 根據 ADT 的定義,在上述兩個範例中有以下 ADT 的 features: 1. 定義一組資料:有 balance、isOpen。 2. 定義一組操作:deposit()、withdraw()、getBalance()、open()、close()。 3. 隱藏實作細節:透過封裝的方式讓 user 無法直接操作資料,只能透過介面(像是由開發者所提供的成員函式 deposit() 等)去操作。 4. 資料結構的獨立性:就算用不同資料結構去做這些事情,介面一樣不會變。 :::info ADT features: - Abstraction(抽象) - Better Conceptualization(更佳的概念化) - Robust(茁壯性,程式能有效捕捉到錯誤) - Encapsulation(封裝) - Data Abstraction - Data Structure Independence - Information Hiding - Modularity(模組化) From GeeksForGeeks ::: ADT 的經典例子 --- 那些常見的資料結構如: 1. 鏈結串列 list 2. 堆疊 stack 3. 佇列 queue 同屬於 ADT。 這邊就舉 stack 跟 queue 當例子,why?因為實作起來比較簡單,~~我恨 list~~。 ```cpp= // stack.cpp #include <iostream> #include <vector> using namespace std; class Stack { private: vector<int> data; public: void push(int x) { data.push_back(x); } void pop() { if (!data.empty()) data.pop_back(); } int top() { return data.back(); } bool isEmpty() { return data.empty(); } }; ``` ```cpp= // queue.cpp #include <iostream> #include <vector> using namespace std; class Queue { private: vector<int> data; int frontIndex; public: Queue() { frontIndex = 0; } void enqueue(int x) { data.push_back(x); } void dequeue() { if (!isEmpty()) { frontIndex++; } } int front() { if (!isEmpty()) { return data[frontIndex]; } else { // cpp 例外處理語法 throw out_of_range("Queue is empty"); } } bool isEmpty() { return frontIndex >= data.size(); } int size() { return data.size() - frontIndex; } }; int main() { Queue q; q.enqueue(10); q.enqueue(20); q.enqueue(30); cout << "front : " << q.front() << endl; q.dequeue(); cout << "front : " << q.front() << endl; q.dequeue(); cout << "Is queue empty : " << (q.isEmpty() ? "y" : "n") << endl; q.dequeue(); cout << "Is queue empty : " << (q.isEmpty() ? "y" : "n") << endl; return 0; } ``` 總結 --- ### 資料抽象(Data Abstraction)概念 資料抽象是指只向外界提供必要的資料資訊,隱藏不必要的細節和實作方式。如遊戲中玩家只看到角色的HP、MP 等狀態,不用知道這些數值如何計算或存放在記憶體的哪裡。開發者透過類別(class)封裝內部資料與操作,並只公開必要的介面給使用者,達成資料抽象。 ### 資料抽象的優點 - 簡化程式設計:隱藏內部實作細節,只暴露必要介面,讓使用者專注於功能使用,讓程式可讀性提升也好管理。 - 提高可維護性:內部實作可變更而不影響外部使用者,只要介面不變,減少維護成本與錯誤風險。 - 保護資料完整性:將資料成員設為 private,避免外部直接修改,防止狀態不一致或錯誤,提升安全性與穩定性。 - 促進程式碼重用:抽象類別設計模組化,能在不同專案或上下文中重複使用,提高開發效率。 ### 封裝與抽象的關係 抽象是設計概念,強調「做什麼」而非「怎麼做」。 封裝是實現抽象的技術手段,利用存取修飾子(public、private、protected)控制資料存取權限,隱藏實作細節。 ### 抽象資料型態(Abstract Data Type, ADT) ADT 是一種概念模型,定義一組資料和操作行為,但不規定其內部實作或資料組織方式。C++ 中的類別都是屬於 ADT 的實作。ADT 的特性包括: - 定義資料集合(如 balance、isOpen) - 定義操作行為(如 deposit()、withdraw()、open()、close()) - 隱藏實作細節,使用者只能透過介面操作資料 - 資料結構獨立性,內部實作可變更但介面不變。 ADT 的經典例子含鏈結串列(list)、堆疊(stack)、佇列(queue)等。 參考資料 --- [單元二 資料抽象化 Data Abstraction - HackMD](https://hackmd.io/@Derek46518/BkqWS_Hmj) [Data Structure 資料結構 - Abstraction 抽象化 | by Fion Yu | Minds | Medium](https://medium.com/fion-minds/data-structure-%E8%B3%87%E6%96%99%E7%B5%90%E6%A7%8B-abstraction-%E6%8A%BD%E8%B1%A1%E5%8C%96-511ed86fbd1c) [Abstract Data Types - GeeksforGeeks](https://www.geeksforgeeks.org/abstract-data-types/) [Abstraction in C++ - GeeksforGeeks](https://www.geeksforgeeks.org/cpp/abstraction-in-cpp/) [C++ 数据抽象 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-data-abstraction.html)