1. 程式人生 > >C++沉思錄 第八章 面向物件程式範例

C++沉思錄 第八章 面向物件程式範例

c++沉思錄第八章的示例程式很有意思。程式雖小,卻很好地詮釋了面向物件程式設計的思想
正如書上說的仔細研究還是有所收穫的。

先上程式碼

code

Expr_node.h

#pragma once
#include "Expr.h"
#include<string>

using namespace std;

//Expr_node 基類
class Expr_node {
    friend class Expr;
    int use;
public:
    Expr_node() :use(1){}
    virtual ~Expr_node(){}
    virtual
void print(std::ostream&) const = 0; }; class Int_node :public Expr_node{ int n; void print(ostream& o) const { o << n; } public: Int_node(int k):n(k){} }; class Unary_node :public Expr_node{ //friend class Expr; string op; Expr opnd; void print(ostream& o) const
{ o << "(" << op << opnd << ")"; } public: Unary_node(const string& a, Expr b) :op(a), opnd(b){} }; class Binary_node :public Expr_node{ //friend class Expr; string op; Expr left; Expr right; void print(ostream& o) const { o << "("
<< left << op << right << ")"; } public: Binary_node(const string& a, Expr b, Expr c) :op(a), left(b), right(c){} };

Expr_node.cpp


#include "stdafx.h"
#include "Expr_node.h"

控制代碼類Expr

Expr.h

#pragma once
#include<iostream>

using namespace std;
class Expr_node;
class Expr{
    friend ostream& operator<<(ostream&, const Expr&);
    Expr_node* p;
public:
    Expr();
    Expr(int);
    Expr(const string&, Expr&);
    Expr(const string&, Expr&, Expr&);

    Expr(const Expr& t);
    Expr& operator=(const Expr&);
    ~Expr();
};

Expr.cpp

#include "stdafx.h"
#include "Expr.h"
#include "Expr_node.h"

Expr::Expr()
{
}

Expr::~Expr()
{
    if (--p->use == 0)
        delete p; 
}
Expr::Expr(int n)
{
    p = new Int_node(n);
}
Expr::Expr(const string& op, Expr& t)
{
    p = new Unary_node(op, t);
}
Expr::Expr(const string& op, Expr& left, Expr& right)
{
    p = new Binary_node(op, left, right);
}
Expr::Expr(const Expr& t)
{
    p = t.p; ++p->use;
}

Expr& Expr::operator=(const Expr& rhs)
{
    rhs.p->use++;
    if (--p->use == 0)
        delete p;
    p = rhs.p;
    return *this;
}
ostream& operator<<(ostream& o, const Expr& t)
{
    t.p->print(o);
    return o;
}

測試程式:

Ooptest.cpp

#include "stdafx.h"
#include<iostream>
#include<string>
#include "Expr.h"

int main(int argc, char* argv[])
{
    //Expr a = Expr("-", Expr(5));
    //Expr b = Expr("+", Expr(3),Expr(4));
    Expr t = Expr("*", Expr("-", Expr(5)), Expr("+", Expr(3), Expr(4)));
    std::cout << t << std::endl;
    return 0;
}

分析:

這裡總共3個類。

Expr
這個稱之為控制代碼類。其實可以理解為馬甲~~-.-

其作用是幫助管理了記憶體分配,引用計數等問題。

Expr_node

3種表示式的基類。
抽象出print等函式,因為具體的列印應該由具體的表示式來輸出。
提供use成員變數來計數。其實這個變數是為了給Expr用的。

當2個Expr_node物件相互賦值時,由Expr這個控制代碼類來管理計數值,比如a=b,b引用的物件應該+1,因為現在a也來引用了。而a原來引用的應該-1,因為a找到了新的物件。。。當某個物件引用計數為0時,Expr就delete掉這個物件,這個所有的過程都是Expr完成的。

其實這裡還有幾個語法點
這裡的程式和書上的略有不同。
比如:


class Unary_node :public Expr_node{
    //friend class Expr;
    string op;
    Expr opnd;
    void print(ostream& o) const
    {
        o << "(" << op << opnd << ")";
    }
public:
    Unary_node(const string& a, Expr b) :op(a), opnd(b){}
};

書上用到了friend,友元的概念。

這裡的意思是把Expr宣告為Unary_node的友元類,那麼會有什麼變化呢?

這個效果就是Expr可以訪問Unary_node的的隱藏資訊包括 私有成員和保護成員了。

然後Expr.cpp裡的這行程式碼就合法了。因為建立物件要呼叫建構函式,而這個建構函式是private的。

p = new Unary_node(op, t);

我這裡加上public,就可以註釋掉那一行了。

再看下Expr.h

class Expr_node;
class Expr{
    friend ostream& operator<<(ostream&, const Expr&);
    Expr_node* p;

這裡只是聲明瞭下 Expr_node,因為下面只是用到了Expr_node*
而沒有

#include "Expr_node.h"

這裡又涉及到了標頭檔案包含的技巧,以及編譯器如何來編譯的問題。

這樣可以避免標頭檔案包含來包含去的,免得又是一堆編譯報錯。

不過後面在Expr.cpp。還是需要include進來。

end