讀《C++沉思錄》有感

關于類的設計:代理類

《C++沉思錄》的原話是這樣的

我們怎樣才能設計一個C++容器,使它有能力包含類型不同而彼此相關的對象呢?

我們從為什么需要這么一個容器開始討論。

假設我們要設計一個停車場,這個停車場就是一個容器。那么停車場需要停各種不同的車輛,不同的車輛就是不同的類,但他們都是有關系的(都是交通工具)。我們知道,C++標準的容器中儲存的都是相同類型的類,例如數(shù)組,vector......那么原有的容器就無法滿足我們停車場的需求了。 所以我們就需要一個能包含類型不同而彼此相關的對象(車)

下面我們來模擬整個流程

  • 在有停車場之前,要先有車。因此,首先需要一個抽象基類,命名為Vehicle。它有一系列的派生實類:Automobile,Truck......
class Vehicle{ 
public:
           virtual double weight() const = 0;
           virtual void start() = 0;
           // ...
};
class Automobile: public Vehicle {/*...*/};
class Truck: public Vehicle {/*...*/};
......

  • 現(xiàn)在我們來模擬停車場(容器)。這個停車場到底需要什么功能呢?
    (1)停車場實際上是不需要知道到底是什么車停進來的,只需要知道它是車。
    (2)有車進來的時候,我們能跟蹤它,給一個車位(內存)給它。
    (3)車換位置停的時候,我們要知道它換到哪了。
    (4)當車離開的時候,我們要把車位(內存)釋放。
    我們通常的做法是用一個指針數(shù)組
Vehicle* parking_lot[1000];
Automobile x = /*.....*/;
parking_lot[num_vehicles++] = &x;
//num_vehicles means numbers of vehicles

這么做有一個弊端,這個指針是直接指向車本身的。打個比方,假如車開出了停車場,理論上來說,這個指針還會跟著車走,但我的指針是屬于停車場的,出不去,那么車開出去的時候這個指針指向哪里就out of control

既然這樣,那我們來做第一個變通。我們不讓指針指向車本身,我們指向它的一個副本。

Automobile x = /*.....*/;
parking_lot[num_vehicles++] = new Automobile(x);

我簡單解釋一下第二行等號右邊代碼的意思:new操作符分配了一塊內存(車位),返回指向這塊內存的指針,大小為Automobile這么大;Automobile(x)是一個復制構造函數(shù),返回值是一個和x一樣的類。
這個做法有兩個弊端:1. 增加顯示動態(tài)內存管理的負擔。2. 我需要確切知道它是什么類型。 但實際上,停車場并不需要它到底是哪款車型,只要知道有車進來就行了。


如果代碼是這樣的,就很簡潔了

Automobile x = /*.....*/;
parking_lot[num_vehicles++] = x;

不需要顯示的處理內存,不需要判斷車的類型。


如何做到既能避免顯示的處理內存分配,又能保持類在運行時綁定的屬性呢?
解決這個問題的關鍵是要用類來表示概念,這在C++中是很常見的。我總是把這一點當作最基本的C++設計原則。在復制對象的過程中運用這個設計原則,就是定義一個行為和Vehicle對象相似,而又潛在的表示了所有繼承自Vehicle類的對象的東西,我們把這種類的對象叫做代理(surrogate)

講到這里,相信大家都應該明白,實際上,停車場需要操作的實際上是車位,并不是車輛。車位,是一個跟車輛綁定的東西。在這個例子中,我們可以把車位理解成車輛類的代理。


無論是第一種變通辦法還是定義代理,我們都需要一個操作,就是復制copy(),因此,我們需要更新一下車輛類的定義

class Vehicle{ 
public:
           virtual double weight() const = 0;
           virtual void start() = 0;
           virtual Vehicle* copy() const = 0;
           virtual ~Vehicle() { }
           // ...
};

Vehicle* Automobile::copy() const{
          return new Automobile(*this);
}
......

有了虛函數(shù)copy來完成復制工作,那么代理類(車位)就比較好寫了:

class VehicleSurrogate{
public:
          VehicleSurrogate();
          VehicleSurrogate(const Vehicle&);
          ~VehicleSurrogate();
          VehicleSurrogate(const  VehicleSurrogate&);
          VehicleSurrogate& operate=(const VehicleSurrogate&);
          //來自類Vehicle的操作
          double weight() const;
          void start();
          //...
private:
          Vehicle* vp;
}

值得注意的是,在代理類(車位)中,我們重載了賦值符‘=’。目的是為了后續(xù)代碼的簡潔。(上述代碼只給出了定義,具體實現(xiàn)比較簡單,需要的私聊)


完成了上述的工作,我們的停車場基本就很容易定義了。

VehicleSurrogate parking_lot[1000];
Automobile x;
parking_lot[num_vehicles++] = x;

最后一行代碼的原型是:
parking_lot[num_vehicles++] = VehicleSurrogate(x);
我們重載的賦值符 ‘=’ 的好處就出現(xiàn)了,使代碼變得更加簡潔明了。


最后,當然要埋下伏筆啦。什么伏筆呢?
相信細心的讀者也發(fā)現(xiàn),涉及到代理就離不開復制,但是復制一個類的代價有時候是很大的,是我們不愿意的支付的,那我們如何避免這些復制呢?希望讀者也能思考思考。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 今天翻出去年初寫的一篇競品分析,發(fā)上來做個紀念!現(xiàn)在看看有很多不成熟的地方,分析的思路也不足夠清晰。不過其中有一個...
    OD張閱讀 8,794評論 2 26
  • 停車產品市場趨勢與格局 互聯(lián)網對停車市場的改造已經歷了很多年,但就目前而言,始終處于不溫不火的狀態(tài)。2010年前后...
    shxian閱讀 6,954評論 1 30
  • 領域驅動設計(DDD)旨在軟件設計過程中提煉領域模型,以領域模型為核心改善業(yè)務專家和軟件開發(fā)者的溝通方式,對企業(yè)級...
    MagicBowen閱讀 6,035評論 0 29
  • 1.dSYM你是如何分析的? 2.多線程有哪幾種?你更傾向于哪一種? 3.單例弊端? 4.如何把異步線程轉換成同步...
    xiaon閱讀 1,647評論 0 2

友情鏈接更多精彩內容