1. 程式人生 > >C++筆記:面向物件程式設計(Handle類)

C++筆記:面向物件程式設計(Handle類)

控制代碼類

控制代碼類的出現是為了解決使用者使用指標時需要控制指標的載入和釋放的問題。用指標訪問物件很容易出現懸垂指標或者記憶體洩漏的問題。

為了解決這些問題,有許多方法可以使用,控制代碼類就是其中之一。控制代碼類是一種包裝類,用於儲存和管理基類的物件指標,減輕使用者使用物件的負擔。控制代碼類使用指標執行操作,虛成員由於既可以指向基型別又可以指向派生型別,所以其行為將在執行時根據控制代碼實際繫結的物件而變化。

控制代碼類的設計有兩個重要的考慮因素:

  • 必須確定複製控制
  • 是否遮蔽繼承層次(不遮蔽使用者需要了解基類物件的使用)

指標型控制代碼

  • 指標型控制代碼可以像指標一樣,將控制代碼類handler繫結到base型別物件上,並使用*和->執行base型別物件的操作,使用者則不必管理handler的指向。指標型控制代碼將暴露所有的繼承層次。
  • 控制代碼類需要三個建構函式:預設建構函式、複製建構函式和使用base型別物件的建構函式。控制代碼類將保證當控制代碼物件存在時,base型別物件副本就存在;且使用控制代碼物件給控制代碼物件賦值時,將複製指標,而不是物件。除定義三個建構函式外,控制代碼類還應該定義解引用操作符和箭頭操作符,這樣可以達到將控制代碼類繫結到base型別物件上的目的。
  • 控制代碼類同樣使用計數來管理副本。控制代碼類中使用計數指標成員可以使多個控制代碼類物件可以共享同一計數器。

指標型控制代碼例子

class Sales_item { //Sales_item 為handle類
public:
    Sales_item():p(0), use(new size_t(1)) {}
    Sales_item(const Item_base& item): p(item.clone()), use(new size_t(1)) {}
    Sales_item(const Sales_item& item):p(item.p), use(item.use) { ++*use; }
    ~Sales_item() { decr_use(); }

    Sales_item& operator=(const Sales_item&);
    const Item_base* operator->() const;
    const Item_base& operator*() const;

private:
    void decr_use();
private:
    Item_base *p;
    size_t *use;
};

Sales_item& Sales_item::operator=(const Sales_item& rhs)
{
    ++*rhs.use;
    decr_use();
    p = rhs.p;
    use = rhs.use;
    return *this;
}

const Item_base* Sales_item::operator->() const
{
     if(p)
      return p;
     else
      throw logic_error("unbound Sales_item");
}

const Item_base& Sales_item::operator*() const
{
    if(p)
      return *p;
    else
      throw logic_error("unbound Sales_item");
}

void Sales_item::decr_use()
{
    if( --*use == 0 )  
    {
        delete p;
        delete use;
    }
}

這個控制代碼類中要求Item_base類中有一個虛擬函式clone,這個虛擬函式的用途是解決基型別物件或者派生型別物件的複製,這樣可以不用為控制代碼類針對每一種派生型別物件建立建構函式。
class Item_base{
public:
    virtual Item_base* clone() const { return new Item_base(*this); }
};

以上程式碼即可將Item_base類的指標包裝起來。通過對Handler類物件的*和->操作,即可直接訪問到所包裝的Item_base型別物件。

值型控制代碼

  • 部分時刻使用者程式碼不能直接使用控制代碼定義的繼承層次,使用者程式碼必須通過控制代碼操作Base型別物件。像一個代理物件(Proximity)一樣,控制代碼類提供眾多的對Base型別操作的成員函式及操作符。
  • 值型控制代碼由於常常直接參加運算子與函式操作,而這些運算子和函式中常常會訪問到控制代碼類中的保護部分(protected和private),所以需要在控制代碼類中將這些運算子與函式都加為友元,使控制代碼類對自定義的運算子和函式開放。
  • 控制代碼類中同樣保持指向基類物件和計數器的兩個指標。指標銷燬條件和控制代碼建構函式都與指標型控制代碼相同。但不同的是,值型控制代碼不定義*和->兩個操作符,基類物件指標完全封閉在控制代碼類中。
  • 推薦值型控制代碼通過其派生類和基類派生類來完成各種值型操作,保持面向物件的設計風格。

值型控制代碼例子

class Query {
    friend Query operator~ (const Query&);
    friend Query operator| (const Query&, const Query&);
    friend Query operator& (const Query&, const Query&);

private:
    Query(Query_base *query): q(query), use(new size_t(1)) {  }
public:
    Query(const string& s): q(new WordQuery(s)), use(new size_t(1)) {  }  
    Query(const Query &c): q(c.q), use(c.use) { ++*use; }
    Query& operator= (const Query&);
    ~Query(){ decr_use(); }

    set<line_no> eval(const TextQuery &t) const
    { 
        return q->eval(t);
    }

private:
    void decr_use();

private:
    Query_base *q;
    size_t *use;
};

void Query::decr_use()
{
    if ( --*use == 0 ) 
    {
        delete q;
        delete use;
    }
}

Query& Query::operator=(const Query& rhs)
{
    ++*rhs.use;
    decr_use();
    q = rhs.q;
    use = rhs.use;
    return *this;
}

值型控制代碼不定義*和->兩個操作符,基類物件指標完全封閉在控制代碼類中。
參考: