惡草叢生的陰暗角落---虛擬機器制(下) (轉)

worldblog發表於2008-01-21
惡草叢生的陰暗角落---虛擬機器制(下) (轉)[@more@]

  虛的開銷
  人們一提到虛擬函式,首先想到的是多型,緊接著想到的就是開銷(至少我開始的時候就是這樣子的),那麼虛擬函式的開銷來自哪裡?開銷究竟有多大?
  在理論上來講,虛擬函式所帶來的動態開銷主要依賴於3個方面:、操作和機器。但是在現實中,幾乎所有的編譯器都以同樣的方式操作。一個虛擬函式的開銷主要來自於2個方面,一個是如果虛擬函式不是內聯的,就要增加一些額外的機器指令,不過一般來說也就增加3-5個機器指令(是從那裡看的,既不清楚了,不過這個結論我倒是記得很清楚),從時間上來講,與一個非虛擬函式相比,也就多花費10%-20%的開銷,如果有幾個引數,這個比例會更小。同時,函式呼叫的連線開銷通常只是總的開銷的一小部分,所以基本上來講,序函式的開銷可以忽略不計。當然,如果你在中大量運用虛擬函式,自然就會有很大的下降,積少成多麼。

  虛解構函式的時機
  解構函式能不能是虛擬的,這個問題大家都比較清楚:能!那麼什麼時候把解構函式設計成虛擬的呢? 
  初學者往往會犯這樣的錯誤:基類解構函式當然應該始終是虛擬的!
  看來這個規則比較的容易記住,當然她也有自己的理論基礎(雖然分析的不怎麼全面):一個類既然可以作為基類,那麼他就打算多型的使用,因此就應該將解構函式設成虛擬的;另外由於目前大多數編譯器都採用vtbl的手法實現虛擬函式,而vtbl是一個類共享的,所有的類的例項都共享這個vtbl,換句話說,如果一個類裡面已經有了一個虛擬函式,那麼把解構函式宣告為一個虛擬函式不會對每個例項有什麼空間開銷。
  不過這樣的分析還是不準確,例如下面的例子
  class Base
  {
  public:
  ~Base() {cout<  };

  class Derive:public Base
  {
  public:
  ~Derive() { cout<  };
  Derive d;
  看看他有沒有呼叫~Base?Base雖然作為基類,但是它的解構函式並不需要宣告為虛擬的。那麼什麼時候才必須宣告成虛擬的呢?
  更加準確的規則如下:如果派生類有一個特殊的解構函式,並且我們也需要動態的刪除基類的指標,那麼這個基類的解構函式就應該是虛擬的。

  虛建構函式
  既然我們有了虛解構函式,那麼有沒有虛建構函式呢?
  很不幸的是,沒有!原因在於虛擬呼叫是一種能夠在給定資訊不完全的情況下工作的機制。特別地,虛擬允許我們呼叫某個函式,對於這個函式,僅僅知道它的介面,而不知道具體的物件型別。但是要建立一個物件,你必須擁有完全的資訊。特別地,你需要知道要建立的物件的具體型別。因此,對建構函式的呼叫不可能是虛擬的。
  不過,還能夠給大家一個安慰的是,我們可以模擬虛建構函式。
  在TC++PL第15.6.2.節Bjarne大師給出了一個例子。無獨有偶,在《Thinking in C++》的作者也給出了一種模擬虛建構函式的方法,基本是這樣的。
  // 給出一個抽象類shape,裡面有要提供的介面
  class shape
  {
  public:
    shape();
    virtual ~shape();
    virtual void draw();
    // ....
  };

  class circle : public shape
  {
  public:
    circle(); ~circle();
    void draw();
    // ...
  };

  class rectangle : public shape
  {
  public:
    rectangle(); ~rectangle();
    void draw();
    // ...
  };

  // 再給一個shapewrap封裝一下
  class shapew
  {
  protected:
    shape *;
  public:
    shapewrap(const string &type)
    {
      if (type=="circle")
        object=new circle;
      else if (type=="rectangle")
        object=new rectangle;
      else
      {
        // ...
      }
    }
    ~shapewrap() { delete object; }
    void draw() { object->draw(); }
  };

  為什麼成員函式預設不是virtual的?
  因為很多類並不是被設計作為基類的。例如複數類。
  而且,一個包含虛擬函式的類的物件,要佔用更多的空間以實現虛擬函式呼叫機制——往往是每個物件佔用一個字()。這個額外的字是非常可觀的,而且在涉及和其它語言的資料的相容性時,可能導致麻煩(例如C或Fortran語言)。
  要了解更多的設計原理,可以參考The D&E of C++。

   成員函式的模板可不可以是Virtual的?
  ANSI/ISO 的標準說 (14.5.2 p 3): "A member function template shall not be virtual."(一個成員函式的模板不可以是virtual.)
  


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-997983/,如需轉載,請註明出處,否則將追究法律責任。

相關文章