物件導向程式設計的三要素:資料抽象、繼承以及動態繫結。
這裡討論乙個算術表示式樹問題,如(-5)*(3+4)對應的表示式樹為:
我們希望通過呼叫合適的函式來建立這樣的樹,然後列印該樹完整的括號化形式。例如:
expr t = expr("輸出結果為:((-5)*(3+4))*", expr("
-",5), expr("
+", 3, 4
));cout
<< t << endl;
此外我們不想為這些表示式的表示形式操心,也不想關心它們記憶體分配和**的事宜。
從上面圖我們可以看出,圖中有兩種物件節點和箭頭。每個節點包含乙個值——乙個運算元或者乙個操作符——並且每個結點又具有零個、乙個或者兩個子節點。
這些節點既是相同的又是不同的,我們該如何抽象節點的資料結構呢?
這些節點首先有乙個共同的特點:每個類都要儲存乙個值以及一些子節點。同時也可以很容易看出這些節點的一些不同點,比如它們儲存的值的種類,子節點的數目。
在設計類的時候繼承使得我們可以不中這些類的共同點,而動態繫結又可以幫助各個節點確定身份(從而才去不同的操作)。
仔細分析我們可以把節點分為三種型別:一種表示整數表示式,包含乙個整數值,無子節點。另外兩種分別表示一元表示式和二元表示式(包含乙個操作符,分別有乙個或者兩個子節點)。因為這些節點的不同,我們在列印的時候也要採取不同的操作,這時候就可以使用動態繫結了:我們可以定義乙個virtual 函式來指明應當如何列印各種節點。
注意到這三種節點都成為節點,我們可以先定義乙個共同的基類:
class由於expr_node 類是乙個虛基類,不能例項化物件,只有從其中派生類來例項化物件。expr_node 類的存在只是為了獲得公共的介面。expr_node
};
接著我們先來定義輸出操作符,它要呼叫「對應」的print函式:
ostream & operator按照前面對三種節點的分析,可以很快定義出第一種節點:<< ( ostream & o, const expr_node &e)
class int_node : public對於一元表示式或者二元表示式,因為其中包含有子節點,這時候我們並不知道子節點的型別會是什麼,因此不能按值儲存子節點,必須儲存指標。expr_node
void print(ostream & o) const
};
class unary_node : public從現有的定義來看,如果我們要建立下面的表示式樹:expr_node
void print(ostream & o) const };
class binary_node : public
expr_node
void print( ostream & o) const
};
expr t = expr( "*", expr("-",5), expr("+",3,4) );
則需要像下面一樣來實現:(建立一元和二元表示式樹的建構函式期望獲得指標,而不是物件)
binary_node *t = new binary_node("這個改進離我們理想的表達方式還有差距,並且我們不再擁有指向內層new的物件的指標,因此上述**的情形會造成記憶體洩露,如果我們通過定義好析構函式來解決這個問題,則又可能會多次刪除物件,因為理想情況下可能有多個expr_node指向同乙個下層表示式物件,這種形式把記憶體管理的事情都交給了使用者。*", new unary_node("
-",new int_node(5), new binary_node("
+", new int_node(3), new int_node(4
) );
既然使用者關心的只是樹,而不是樹中的單個節點,就可以用expr來隱藏expr_node的繼承層次。這裡又回到了前面討論的控制代碼類的內容,使用者可見的只有expr。既然使用者要乘船的是expr 而不是expr_node, 我們就希望expr的建構函式能代表所有3種expr_node。每個expr建構函式豆漿建立expr_node的派生類的乙個合適物件,並且將這個物件的位址儲存在正在建立的expr物件中。expr 類的使用者不會直接看到expr_node 物件。
classexpr
};
expr :: expr(int現在使用expr 為 expr_node 分配記憶體,我i類避免不必要的複製,我們依然維護乙個引用計數,但這裡引用計數包含在expr_node 裡面,expr 類和 expr_node 類系統管理引用計數,因此expr 需要作為expr_node的友元出現。n)expr :: expr(
const
string&op, expr t)
expr :: expr(
const
string &op, expr left, expr right)
class當expr 類「複製」乙個expr_node 時,該expr 將其引用計數增1,當引用為0的時候刪除底層的expr_node:expr_node
virtual
void print( ostream &) const = 0
;
virtual ~expr_node()
};
expr 類需要增加複製建構函式,析構函式和賦值函式:
class針對expr 我們還需要定義輸出操作符:expr
~expr()
expr & operator = (const expr &t);
};expr & expr :: operator = (const expr &rhs)
ostream & operator最後需要更改每個派生自expr_node的類,令其操作為私有,將expr 類宣告為友元,儲存expr而不是expr_node的指標,例如:<< (ostream & o, const expr &t)
class binary_node : public如果我們需要對表示式求值,在現在的構架下也很容易實現,可以在 expr 類中增加 eval 成員方法,eval 可以將實際的求值工作委託為做出expr 的結點來完成。expr_node
;
class針對expr_node 派生的每乙個類新增乙個函式來實現求值運算。(這裡就不單獨列出)expr
//新新增的
};這樣 expr_node 類就需要新增另乙個純虛函式:
class
expr_node
;
將全部**列出:
/*執行結果:既然使用者關心的只是樹,而不是樹中的單個節點,就可以用expr來隱藏expr_node的繼承層次。
這裡又回到了前面討論的控制代碼類的內容,使用者可見的只有expr了,記憶體管理的事情就完全由expr掌控!
改進後**如下:
*/#include
#include
using
namespace
std;
class
expr_node
virtual ~expr_node() {}};
class expr //
控制代碼類;
~expr()};
class int_node: public
expr_node
void print(ostream &o) const
int eval() const };
class unary_node: public
expr_node
void print(ostream &o) const
int eval() const};
class binary_node: public
expr_node
void print(ostream &o) const
int eval() const};
expr::expr(
int n)
expr::expr(
const
string& op, expr t)
expr::expr(
const
string &op, expr left, expr right)
expr::expr(
const expr& t)
expr& expr::operator=(const expr&rhs)
ostream& operator
<
void
main()
/*這個例子很好的展示了物件導向的三個要素,這樣設計出的類具有很好的擴充套件性,比如再增加有多個子節點的節點,
只要新增個新類,然後在expr中新增個新建構函式就行了。使用者完全不必知道底層的**是怎麼實現的。以後面對
問題的時候要好好的借鑑這種思想!
*/
c:\windows\system32\cmd.exe /c chap8_expr2.exe
((-5)*(3+4))
-35hit any key to close this window...
《C 沉思錄》 第八章 乙個物件導向程式範例
node.h ifndef node h define node h include expr.h include include using namespace std class expr node virtual expr node virtual void print ostream con...
沉思錄 乙個墮落上進者的自我救贖
人生可以歸結為一種簡單的選擇 不是忙著活,就是忙著死。肖申克的救贖 古人云 吾當三日而自省吾身。意思就是說每個人都需要自我反思。首先我們要相信群眾的眼睛是雪亮的,乙個人好與不好並不是自己說了算,而是他人說的和與昨天的自我對比。lz畢業差不多快一年了,遙想剛剛畢業時的豪情壯志雖仍記憶猶新,但早已拋之九...
沉思錄一 如何維護乙個複雜的網路應用
寫下這個文章的時候,剛從乙個複雜的linux服務端網路應用專案中脫出,除去身心的疲憊不堪後,不得不反思標題中的問題,如何破局?這樣在下次面對相似問題時,可能就多幾分倖存的機率。最想知道我是在什麼地方死的,這樣我就可以避開死亡了。這個是查理芒格多次講過的話。但是很多人被困難問題擊敗後,都沒有找到自己是...