深度探索C++對象模型--Function語意學

成員函數調用方式

Nonstatic Member Function(非靜態成員函數)

-Nonstatic Member Function設計準則:與一般非成員函數(nonmember function)有相同的執行效率。因此,實際上編譯器內部將Nonstatic Member 函數實體轉化爲對等的nonmember 函數實體。
-轉換過程:
1. 添加額外this指針,提供數據存取管道
2. 修改函數體內成員數據存取方式,改爲使用this指針存取
3. 對函數名字重新“mangling”修飾處理

class A{
public:
    int x, y;
    int func();
}
int A::func(){ return x+y; }      //Nonstatic Member function
int A::func(A *const this)        //1.添加額外參數this指針
int A::func(A *const this){
    return this->x + this->y;}    //2.改爲由this指針存取成員數據
extern int func_1AFv(A *const this) //3.對函數名進行“mangling”修飾

obj.func() ---> func_1AFv(&obj)

名稱特殊處理(name mangling):

member function重載化,以提供獨一無二名稱,採用該手法。

+++
//可能的修飾方法:
int Point::foo(float x)   ---> int foo_5PointFf(float x)
int Point::foo(void)   ---> int foo_5PointFv()

Static Member Function(靜態成員函數)

-靜態成員函數沒有this指針:
 1. 不用直接存取class中nonstatic members
 2. 不能被聲明爲const, volatile 或者virtual
 3. 不需要有class object才能被調用

-Static Member Function會被編譯器轉化爲nonmember形式,實際上該類函數產不多等同於nonmember function。
static int A::func()   //靜態成員函數
//各種方式調用靜態函數,均轉換成通過類作用於直接調用
ptr->func()/obj.func()/A::func() --> A::func()
//函數mangling修飾
A::func() ---> func_1ASFv()  //SFv表示靜態成員函數,參數void

Virtual Member Function(虛成員函數)

//1是virtual table slot索引,關聯到func函數
ptr->func() -->(*ptr->vptr[1])(ptr)
//明確的調用可以抑制虛擬機制
A::func()
// 通過該obj調用不支持多態
obj.func() ---> func_1AFv(&obj)

-通過class object調用virtual function的操作會被編譯器轉換爲一般的nonmember member function,不呈現多態。

下面將虛函數的調用。

Virtual Member Functions

c++多態:以一個public base class的指針,尋址出一個derived class object。

//調用虛函數,如何在執行期確定func()實體
ptr->func()
  • 編譯期間完成的事情:
    -給每一個class object安插由編譯器內部產生的vptr
    -給vtbl每一個virtual function一個固定的slot,在整個繼承體系中保持與特定virtual funciton相聯

  • vtbl包含的virtual function有:
    -該class所定義的函數實體,包括覆蓋base class中的virtual function
    -繼承自base class的實體,並決定不覆蓋
    -pure_virtual_called()純虛函數

單一繼承

class Point {
public:
    virtual ~Point();   //slot 1
    virtual Point& mult (float) = 0   //slot 2
    float x() const {return _x;}
    virtual float y() const {return 0;}   //slot 3
    virtual float z() const {return 0;}   //slot 4
protected:
    Point (float x = 0.0);
    float _x;   
}

class Point2d:public Point {
public:
    ~Point2d();   //slot 1
    Point2d& mult (float)   //函數覆蓋 slot 2
    float y() const {return _y;}   //函數覆蓋 slot 3
protected:
    float _y;   
}

class Point3d:public Point2d {
public:
    ~Point3d();   //slot 1
    Point3d& mult (float)   //函數覆蓋 slot 2
    float z() const {return _z;}   //函數覆蓋 slot 4
protected:
    float _z;   
}


//一般來說我們事先並不知道ptr所指對象真正類型
//所以不知道哪個mult實體會被調用,但mult()固定在slot 2中是確定的
ptr->mult()
//因此編譯器如下轉換
//執行期唯一需要做的就是確定哪一個slot 2的實體
(*ptr->vptr[2])(ptr)

這裏寫圖片描述

多重繼承

//編譯時期就將指針轉移至第二個base class起始地址
Base2 *pbase2 = new Derived;
//執行時需要調整pbase2至Derived起始地址
delete pbase2;   //該行代碼無法在編譯時知道pbase2的真實對象,執行時才知道

-多重繼承結構如下:Derived會有兩個vtbl被編譯器產生:(1)主體實體,與base1(最左端base class)共用 (2)次要實體,與base2有關
這裏寫圖片描述

-三種需要調整指針的情況
1 通過指向base2的指針,調用derived class virtual function。

//編譯時期就將指針轉移至第二個base class起始地址
Base2 *pbase2 = new Derived;
//執行時需要調整pbase2至Derived起始地址
delete pbase2;

2 通過指向derived class的指針,調用第二個base claass中的一個繼承而來的virtual function。

//編譯時期就將指針轉移至第二個base class起始地址
Derived *pder = new Derived;
//執行時需要調整pder以指向Base2 subobject
pder->mumble;   //調用Base2::mumble()

3 三個clone函數實體返回值類型不同

//編譯時期就將指針轉移至第二個base class起始地址
Base2  *pbase2 = new Derived;
//調用Derived* Derived::clone()
//需要將pbase2指針調整至Derived class起始處
//返回值時又需要重新調整至Base2 subobject
Base1 *pbase2_new = pbase2->clone;

虛擬繼承

詳見鏈接:
http://blog.csdn.net/isunn/article/details/45535951

指向Member Function指針

指向nonvirtual function指針

-得到真實地址,但它需要綁定與某個class object上,才能通過它調用。

double (Point::*coord) = &Point::x;  //nonvirtual function
//調用函數
(obj.*coord) ()
//被編譯器轉化
(coord)(&obj)  //&obj爲this指針

-static member function函數地址爲普通函數指針,因爲沒有this指針。

指向virtual function指針

virtual function地址在編譯時期是未知的,但是與之相關的vtbl slot的索引值是固定的。所以取virtual function地址只能獲得其索引值。

class Point{
public:
    virtual ~Point();   //slot 1
    virtual float z();  //slot 2
}
float (Point::*pmf) = Point::z;   //= 2
Point *ptr = new Point2d;
ptr->*pmf;
//被編譯器轉化爲
(*ptr->vptr[(int)pmf](ptr));

多重繼承下 member function指針

問題:如何區別Member Function指針指向地址還是virtual table索引?
方法:修改Member Function指針mptr結構體,mptr->index正負判別。

struct __mptr {
    int delta;
    int index;  //virtual function時:vtbl索引 nonvirtual時爲-1
    union{
        ptrtofunc faddr;   //nonvirtual 時函數地址
        int       v_offse;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章