mso-ascii-font-family:"Courier New"'>〖輕鬆一刻〗:BMW和一塊帆布
mso-ascii-font-family:"Courier New"'>天要下雨了,車場上一輛BMW,工作人員拿一塊帆布將它蓋得嚴嚴實實。丁小明和南老師經過時看到了這一幕。
mso-ascii-font-family:"Courier New"'>丁小明:“看,那塊帆布就是一種封裝,它保護了底下的BMW"Courier New"'>不受風吹雨打。”
mso-ascii-font-family:"Courier New"'>南鬱:“不,真正的封裝,是BMW自身,這輛車才是完美的封裝藝術”。
"Courier New";mso-hansi-font-family:"Courier New"'>“封裝”有這麼多內容嗎?有些教材,不就是把“publicmso-hansi-font-family:"Courier New"'>、protected"Courier New"'>、private宋體;mso-ascii-font-family:"Courier New";mso-hansi-font-family:"Courier New"'>”一提,就結束了有關“封裝”的內容嗎?讓我們回顧一下本章的曆程。
宋體;mso-ascii-font-family:"Courier New";mso-hansi-font-family:"Courier New"'>
Wingdings;font-weight:normal">Ø 不變式是類的靈魂
"Courier New";mso-hansi-font-family:"Courier New"'>要懂得“封裝”,文法與語義上第一件要理解的是“thismso-hansi-font-family:"Courier New"'>”指標,知道它是被編譯器偷會為成員函數的偷偷加一個入參,就是它。然後要能理解就算是一個看起來一個空空的類,它也會有一些“預設的行為”。
"Courier New";mso-hansi-font-family:"Courier New"'>但是,關於封裝的第一件有“靈魂”的事,是“類的不變式”。讓我們來複習一下這些沒有靈魂的封裝:
Code:
- int x, y;
"Courier New";mso-hansi-font-family:"Courier New"'>說這就是一個座標?這是裸奔。封裝都談不上,哪來的靈魂。
Code:
- struct Point
- {
- int x, y;
- };
"Courier New";mso-hansi-font-family:"Courier New"'>
"Courier New";mso-hansi-font-family:"Courier New"'>代碼閱讀者的心理負擔因為這個結構的存在,而減輕了很多,但它也就如此了, "Courier New"'>不存在更多資訊的封裝。
Code:
- class Point
- {
- public:
-
- int GetX() const { return _x; }
-
- int GetY() const { return _y; }
-
-
-
- void SetX(int x) { _x = x; }
-
- void SetY(int y) { _y = y; }
-
- private:
-
- int _x, _y;
- };
"Courier New";mso-hansi-font-family:"Courier New"'>這是冗餘保護,structmso-hansi-font-family:"Courier New"'>穿上了小馬甲搖身變成class"Courier New"'>,可是我們還是認得出它。
"Courier New";mso-hansi-font-family:"Courier New"'>物件導向技術本來就是用來對付足夠複雜的問題的。如果面對的是一個簡單的程式中的某個簡單的事物(此處的座標點),那麼structmso-hansi-font-family:"Courier New"'>版本的Point"Courier New"'>就是最佳選擇。如果面對的是一個複雜的程式中的一個簡單的對象,冗餘保護的那個版本值得借鑒。
"Courier New";mso-hansi-font-family:"Courier New"'>一個類型,可能有十數個成員資料,但如果這些成員資料之間彼此沒有什麼關係,那麼很可能它就只適合做一個結構;或者,它只有一個內部資料,但它在和外部打交道時,需要在多種狀態之間按照某種規律切換,那麼這個類也需要我們好好去封裝,以維護它的 "Courier New"'>“不變式”。
Wingdings;font-weight:normal">Ø 需要拷貝構造和賦值嗎?
"Courier New";mso-hansi-font-family:"Courier New"'>當一個類在構造對象時,需要分配額外資源,通常就需要一個配套的解構函式,用以在對象消逝前釋放資源。當然,並不是說資源只能在析構時釋放,但通常會是個好時機可以確保資源被安全回收。
"Courier New";mso-hansi-font-family:"Courier New"'>如果僅僅是這樣,那倒也簡單,一對一地寫構造和解構函式就是了;但是想到對象還會以另一個對象為模子建立出來,這就需要一個拷貝建構函式了。再想到對象還可以彼此間賦值,於是要重載賦值操作符,這下子要放下自己原來已有的,然後再接受對方已經有的……這些過程,都要考慮“深拷貝”或“淺拷貝”問題。
"Courier New";mso-hansi-font-family:"Courier New"'>是時候做一個提示了:並不是所有類(的對象)都需要“拷貝”或“賦值”的。比如寫一個視窗(Windows)"Courier New";mso-hansi-font-family:"Courier New"'>程式,會有許多和視窗類別,這些類通過就在需要時構造出一個實際視窗,很少需要進行“拷貝”,或者讓Amso-hansi-font-family:"Courier New"'>視窗賦值給B"Courier New"'>視窗。再比如,我們自己定製的某些容器類(比如前述的IntegerList"Courier New"'>),在實際應用中,有時候沒有複製整條鏈表的需求。
"Courier New";mso-hansi-font-family:"Courier New"'>當某個類不需要 "Courier New"'>“拷貝構造”或“相互賦值”的能力時,可以直接明了的將這兩個函式宣告為私人的:
Code:
- class FontDialog
-
- {
-
- public:
-
- FontDialog(std::string const& title)
-
- : _title(title)
-
- {}
-
-
-
- ~FontDialog() {}
-
- private:
-
- FontDialog(FontDialog const& o);
-
- FontDialog& operator = (FontDialog const& o);
-
-
-
- std::string _title;
-
- };
"Courier New";mso-hansi-font-family:"Courier New"'>注意:我們把FontDialog"Courier New";mso-hansi-font-family:"Courier New"'>的拷貝構造和賦值操作符聲明為私人的,這就代表了外部再也不能調用這兩個函數,既然外部不會調用這兩個函數,自然我們也就不需去真正實現它們。
"Courier New";mso-hansi-font-family:"Courier New"'>下面的代碼通過不過編譯:
Code:
- FontDialog dlgA("Select font ...");
- FontDialog dlgB(dlgA); //編譯不過,因為調用私人的成員函數
-
- FontDialog dlgC("");
- dlgC = dlgA; //也編譯不過,原因同上
mso-ascii-font-family:"Courier New"'>〖小提示〗:STL 容器裡的元素,必須“可複製”
STL的容器,預設採用“值”儲存。並且由於要支援添加,排序等,所以元素之間 經常需要複製,所以想要放入STL"Courier New"'>標準容器的對象,都必須能拷貝及賦值。不過,如果一個對象在實際邏輯上確實不能複製,這時如果又需要放在容器裡,最正確的做法就是只在容器裡儲存它們的指標(相應的,通過需要將元素在堆裡分配)。
-------------------------------------------------------------------------------
如果您想與我交流,請點擊如下連結成為我的好友:
http://student.csdn.net/invite.php?u=112600&c=f635b3cf130f350c