天天看點

Function 語意學

概述

在前面文章《C++對象模型》可以知道,class 中成員函數的類型有:nonstatic member function、static member function、virtual member function;不同類型的成員函數的調用會有不同的表現,以下對每一種類型的成員函數進行簡單的分析。

nonstatic member functions

為了使 nonstatic member function 能夠與非成員函數具有相同的效率,C++ 編譯器内部将 nonstatic member function 轉換為非成員函數,轉換的步驟如下:

  1. 改寫函數的 signature(函數原型)以添加一個 this 指針;
  2. 通過 this 指針存取操作 nonstatic data member;
  3. 将成員函數經過名稱特殊處理轉換為非成員函數;

例如:

class example{
public:
    void func();
};
/* 其中成員函數 func() 會被編譯器内部轉換為 void func(example *this)*/
           

Static member functions

Static member function 和 nonstatic member function 一樣會被編譯器内部轉換為非成員函數,差別是轉換後不存在 this 指針,是以和全局函數一樣,可以做 callback 函數。Static member function 有以下的特性:

  1. 不能直接存取其 class 中的 nonstatic member;
  2. 不能被聲明為 const、volatile 或 virtual;
  3. 一般不需要經由 class object 調用;

virtual member function

獨立的 class

在獨立的 class 中 virtual member function 的調用有兩種方式:指針調用 和 對象調用。例如:

class A {
    virtual void func();
};
A a;
A *pA;
a.func();
pA->func();
           

用指針調用

pA->func()

在編譯器内部被轉換為

( * pA->vptr[1])(pA)

用對象調用

a.func()

在内部被當作和非虛拟成員函數一樣處理,即轉換為

A::func(&a)

;是以 virtual 可以inline,在用對象調用的情況下inline 被展開。

單一繼承的 class

具有 virtual member function 的 class,編譯器對其産生一個 vtable, 把指向虛函數位址的指針放在該表格中;繼承含有 virtual member function 類的派生類具有以下的特性:

  1. 保留 base class 中每個 virtual function 的指針在 vtable 中的索引值及其順序;
  2. 若 derived class 改寫了該 virtual function,則在表格中對應的項上用新函數的位址替換原來函數的位址;
  3. 當 derived class 增加新的虛函數,新的虛函數的位址放在 vtable 中靠後的地方,并不影響表格中已有函數的次序;

多重繼承的 class

像單繼承一樣,用基類的指針或引用隻能通路基類中定義(或繼承)的成員,不能通路派生類中引入的成員。當一個類繼承于多個基類的時候,那些基類之間沒有隐含的關系,不允許使用一個基類的指針通路其他基類的成員。

在多重繼承的 virtual function 機制中,其複雜度圍繞在第二個及後繼的 base class 中,以及在執行期間調整 this 指針;一般規則經由指向第二或後繼 base class 的指針或引用來調用 derived class virtual function。

虛繼承的 class

在虛繼承下,對給定虛基類,無論該類在派生層次中作為虛基類出現多少次,隻繼承一個共享的基類子對象。共享的基類子對象稱為虛基類。

指向成員函數的指針

class A {
    void func();
    virtual void x();
};
           

對非虛函數

void func()

,取它的指針得到的是函數的實際位址(即綁定在某個 class object 的位址),如:

void (A::*pfunc) () = &A::func;

對虛函數

virtual void x()

,取它的位址得到的是

x

class A

的 vtable 中的索引值,如:

viod (A::*pfunc) () = &A::x

;

使用 class object 或指向class object 的指針來調用,即用

a.*pfunc()

pa->*pfunc()

調用;

Inline Function

當我們在 class 中定義 inline function 時,編譯器會根據函數的複雜程度決定是否真正定義為 inline 類型,對不能真正定義為 inline function 的請求,編譯器可能把它們處理為 static member function;真正的 inline function 的擴充操作是在調用的那一點上,這會帶來參數的求值操作以及臨時性對象的管理;

繼續閱讀