成員函數調用方式
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;
}
}