--- title: 封裝(Encapsulation) tags: OOP --- 物件導向特性 - 封裝(Encapsulation) === 示意圖  [圖片來源](https://www.block.tw/blog/arduino-nano-pin/) - 使用模組不需了解其內部詳細運作,只需了解如何使用它。 - 而模組只需提供外部所需的介面,並保證運行正常。 - 對模組而言也不希望外部知道它內部的詳細資訊。 封裝(Encapsulation) === 將物件內部的資料隱藏起來,只能透過物件本身所提供的<font color="#F00">**介面(interface)**</font>取得物件內部屬性或者方法,物件內部的細節資料或者邏輯則隱藏起來,其他物件即無法瞭解此物件的內部細節,若不經過允許之窗口(即此物件提供之方法)便無從更動此物件內之資料。 訪問權限 === | | public | private | protected | |:--------:|:------------------:|:------------------:|:------------------:| | 類別外部 | :heavy_check_mark: | | | | 類別內部 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | 子類別 | :heavy_check_mark: | | :heavy_check_mark: | - 所以對本身類別而言,public就是開放給外面的介面。 - 一般而言,變數基本上設為非public,若要讀寫此變數,須使用public的method操作。 程式範例 === - 假如今天有一個明星,要統計他的得讚數,在同一時間中一個粉絲只能幫喜愛的明星得讚數+1。 若將得讚數設為public權限,若一個粉絲給明星的票數多的時候可能會出現與事實不合得問題。 ```cpp= #include <thread> using namespace std; class SuperStar { public: SuperStar(){} unsigned int likedCount = 0; private: }; void like(SuperStar* role, unsigned int likeCount) { for(unsigned int i = 0; i < likeCount; i++) { role->likedCount++; } } int main(int argc, char *argv[]) { unsigned int likeCount = 1e5, fansNum = 10; SuperStar HatanoYui; thread* fans[fansNum]; for(unsigned int i = 0; i < fansNum; i++) { fans[i] = new thread(like, &HatanoYui, likeCount); } for(unsigned int i = 0; i < fansNum; i++) { fans[i]->join(); delete(fans[i]); fans[i] = nullptr; } cout<<HatanoYui.likedCount<<endl; return 0; } ``` 所以須將其改設為非public,並再寫public的method提供介面,因為此問題為[race condition](https://zh.wikipedia.org/zh-tw/%E7%AB%B6%E7%88%AD%E5%8D%B1%E5%AE%B3),所以可在public的method用mutex解決,讓使用此class的code能更放心的使用。 且更能限制在一個時間內得讚數只能+1。 ```cpp= #include <thread> #include <mutex> using namespace std; class SuperStar { public: SuperStar(){} unsigned int getLikedCount()const; void like(); private: unsigned int likedCount = 0; mutex mutex; }; unsigned int SuperStar::getLikedCount()const { return this->likedCount; } void SuperStar::like() { mutex.lock(); this->likedCount++; mutex.unlock(); } void like(SuperStar* role, unsigned int likeCount) { for(unsigned int i = 0; i < likeCount; i++) { role->like(); } } int main(int argc, char *argv[]) { unsigned int likeCount = 1e5, fansNum = 10; SuperStar HatanoYui; thread* fans[fansNum]; for(unsigned int i = 0; i < fansNum; i++) { fans[i] = new thread(like, &HatanoYui, likeCount); } for(unsigned int i = 0; i < fansNum; i++) { fans[i]->join(); delete(fans[i]); fans[i] = nullptr; } cout<<HatanoYui.getLikedCount()<<endl; return 0; } ``` 若更改需求,可在同一個時間按讚數為任意整數,可將class更改如下,而使用此class的code也會絲毫不受影響。 ```cpp= class SuperStar { public: SuperStar(){} unsigned int getLikedCount()const; void like(int n = 1); private: unsigned int likedCount = 0; mutex mutex; }; unsigned int SuperStar::getLikedCount()const { return this->likedCount; } void SuperStar::like(int n) { mutex.lock(); this->likedCount += n; mutex.unlock(); } ``` 補充: 程式範例使用的mutex來解決race condition的問題,但在此可使用[原子操作(atomic operation)](https://zh.m.wikibooks.org/zh-tw/C%2B%2B/Atomic)會更好,可提高程式效率。 結論 === 封裝限制使用界面最主要是為了讓使用者更安全的使用。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up