C++語言學(xué)習(xí)(十四)——C++類成員函數(shù)調(diào)用分析

C++語言學(xué)習(xí)(十四)——C++類成員函數(shù)調(diào)用分析

一、C++成員函數(shù)

1、C++成員函數(shù)的編譯

C++中的函數(shù)在編譯時(shí)會根據(jù)命名空間、類、參數(shù)簽名等信息進(jìn)行重新命名,形成新的函數(shù)名。函數(shù)重命名的過程通過一個(gè)特殊的Name Mangling(名字編碼)算法來實(shí)現(xiàn)。Name Mangling算法是一種可逆的算法,既可以通過現(xiàn)有函數(shù)名計(jì)算出新函數(shù)名,也可以通過新函數(shù)名逆向推導(dǎo)出原有函數(shù)名。
Name Mangling算法可以確保新函數(shù)名的唯一性,只要命名空間、所屬的類、參數(shù)簽名等有一個(gè)不同,那么產(chǎn)生的新函數(shù)名也不同。
不同的編譯器有不同的?Name Mangling 算法,產(chǎn)生的函數(shù)名也不一樣。

成都創(chuàng)新互聯(lián)主要從事網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)辰溪,10余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792

2、this指針

this指針屬性如下:
A、名稱屬性:標(biāo)識符this表示。
B、類型屬性:classname const
C、值屬性:表示當(dāng)前調(diào)用該函數(shù)對象的首地址。
D、作用域:this指針是編譯器默認(rèn)傳給類中非靜態(tài)函數(shù)的隱含形參,其作用域在非靜態(tài)成員函數(shù)的函數(shù)體內(nèi)。
E、鏈接屬性:在類作用域中,不同類的非靜態(tài)成員函數(shù)中,this指針變量的鏈接屬性是內(nèi)部的,但其所指對象是外部的,即this變量是不同的實(shí)體,但指向?qū)ο笫峭粋€(gè)。
F、存儲類型:this指針是由編譯器生成,當(dāng)類的非靜態(tài)成員函數(shù)的參數(shù)個(gè)數(shù)一定時(shí),this指針存儲在ECX寄存器中;若該函數(shù)參數(shù)個(gè)數(shù)未定(可變參數(shù)函數(shù)),則存放在棧中。
this指針并不是對象的一部分,this指針?biāo)嫉膬?nèi)存大小是不會反映在sizeof操作符上的。this指針的類型取決于使用this指針的成員函數(shù)類型以及對象類型。
類的成員函數(shù)默認(rèn)第一個(gè)參數(shù)為T
const register this。
this在成員函數(shù)的開始執(zhí)行前構(gòu)造,在成員函數(shù)執(zhí)行結(jié)束后清除。

二、C++成員函數(shù)指針

1、C++成員函數(shù)指針簡介

C++語言規(guī)定,成員函數(shù)指針具有contravariance特性,即基類的成員函數(shù)指針可以賦值給派生類的成員函數(shù)指針,C++語言提供了默認(rèn)的轉(zhuǎn)換方式,但反過來不行。
C++編譯器在代碼編譯階段會對類對象調(diào)用的成員函數(shù)進(jìn)行靜態(tài)綁定(虛函數(shù)進(jìn)行動(dòng)態(tài)綁定),類成員函數(shù)的地址在代碼編譯時(shí)就確定,類成員函數(shù)地址可以使用成員函數(shù)指針進(jìn)行保存。
成員函數(shù)指針定義語法如下:

ReturnType (ClassName::* pointerName) (ArgumentLList);
ReturnType:成員函數(shù)返回類型
ClassName: 成員函數(shù)所屬類的名稱
Argument_List: 成員函數(shù)參數(shù)列表
pointerName:指針名稱
class Test
{
public:
    void print()
    {
        cout << "Test::print" << endl;
    }
};

成員函數(shù)指針語法極其嚴(yán)格:
A、不能使用括號:例如&(Test::print)不對。
B、?必須有限定符:例如&print不對,即使在類ClassName作用域內(nèi)也不行。
C、必須使用取地址符號:直接寫Test::print不行,必須寫:&Test::print。
Test類的成員函數(shù)print的函數(shù)指針聲明如下:
void (Test::*pFun)();
初始化如下:
pFunc = &Test::print;
Test類的成員函數(shù)print的函數(shù)指針聲明及初始化如下:
void (Test::* pFunc)() = &Test::print;
通常,為了簡化代碼,使用typedef關(guān)鍵字。

typedef void (Test::*pFunc)();
pFunc p = &Test::print;

可以通過函數(shù)指針調(diào)用成員函數(shù),示例代碼如下:

#include <iostream>

using namespace std;

class Test
{
public:
    void print()
    {
        cout << "Test::print" << endl;
    }
};

int main(int argc, char *argv[])
{
    void (Test::* pFunc)() = &Test::print;
    Test test;
    //通過對象調(diào)用成員函數(shù)
    (test.*pFunc)();//Test::print
    Test* pTest = &test;
    //通過指針調(diào)用成員函數(shù)
    (pTest->*pFunc)();//Test::print
    //pFunc();//error
    //error: must use '.*' or '->*' to call pointer-to-member
    //function in 'pFunc (...)', e.g. '(... ->* pFunc) (...)'

    return 0;
}

上述代碼中,.*pFunc將pFunc綁定到對象test,-&gt;*pFunc綁定pFunc到pTest指針?biāo)赶虻膶ο蟆?br/>成員函數(shù)指針不是常規(guī)指針(保存的是某個(gè)確切地址),成員函數(shù)指針保存的是成員函數(shù)在類布局中的相對地址。

2、C++成員函數(shù)地址

C++成員函數(shù)使用thiscall函數(shù)調(diào)用約定。C++靜態(tài)成員函數(shù)、普通成員函數(shù)的函數(shù)地址在代碼區(qū),虛成員函數(shù)地址是一個(gè)相對地址。

#include <iostream>

using namespace std;

class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
        cout << "Parent(int i, int j): " << this << endl;
    }
    virtual void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    virtual void sayHello()
    {
        cout << "Parent::sayHello()" << endl;
    }
    virtual void func()
    {
        cout << "Parent::func()" << endl;
    }
    virtual ~Parent()
    {
        cout << "~Parent(): " << this << endl;
    }
    static void display()
    {
        cout << "Parent::display()" << endl;
    }
    int add(int v)
    {
        return m_i + m_j + v;
    }
protected:
    int m_i;
    int m_j;
};

int main(int argc, char *argv[])
{
    cout <<&Parent::display<<endl;
    cout <<&Parent::print<<endl;
    cout <<&Parent::sayHello<<endl;
    cout <<&Parent::func<<endl;

    return 0;
}

上述代碼中,打印出的所有的成員函數(shù)的地址為1。原因在于輸出操作符<<沒有對C++成員函數(shù)指針類型進(jìn)行重載,C++編譯器將C++成員函數(shù)指針類型轉(zhuǎn)換為bool類型進(jìn)行了輸出,所以所有的輸出為1。因此,C++成員函數(shù)地址進(jìn)行打印時(shí)不能使用cout,可以用printf輸出,因?yàn)閜rintf可以接收任意類型的參數(shù),包括__thiscall類型。

#include <iostream>

using namespace std;

class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
        cout << "Parent(int i, int j): " << this << endl;
    }
    virtual void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    virtual void sayHello()
    {
        cout << "Parent::sayHello()" << endl;
    }
    virtual void func()
    {
        cout << "Parent::func()" << endl;
    }
    virtual ~Parent()
    {
        cout << "~Parent(): " << this << endl;
    }
    static void display()
    {
        cout << "Parent::display()" << endl;
    }
    int add(int v)
    {
        return m_i + m_j + v;
    }
protected:
    int m_i;
    int m_j;
};

int main(int argc, char *argv[])
{
    //靜態(tài)成員函數(shù)
    cout << "static member function addree:" << endl;
    printf("0x%p\n", &Parent::display);
    printf("0x%p\n", Parent::display);
    //普通成員函數(shù)
    cout << "normal member function addree:" << endl;
    printf("0x%p\n", &Parent::add);
    cout << "virtual member function addree:" << endl;
    //虛成員函數(shù)
    printf("%d\n", &Parent::print);//1
    printf("%d\n", &Parent::sayHello);//5
    printf("%d\n", &Parent::func);//9

    return 0;
}

3、C++編譯器成員函數(shù)指針的實(shí)現(xiàn)

C++編譯器要實(shí)現(xiàn)成員函數(shù)指針,必須解決下列問題:
A、成員函數(shù)是不是虛函數(shù)。
B、成員?函數(shù)運(yùn)行時(shí),需不需要調(diào)整this指針,如何調(diào)整。
不需要調(diào)整this指針的情況如下:
A、繼承樹最頂層的類。
B、單繼承,若所有類都不含有虛函數(shù),那么繼承樹上所有類都不需要調(diào)整this指針。
C、單繼承,若最頂層的類含有虛函數(shù),那么繼承樹上所有類都不需要調(diào)整this指針。
可能需要進(jìn)行this指針調(diào)整的情況如下:
A、多繼承
B、單繼承,最頂?shù)腷ase class不含virtual function,但繼承類含虛函數(shù),繼承類可能需要進(jìn)行this指針調(diào)整。
Microsoft VC對C++成員函數(shù)指針的實(shí)現(xiàn)采用的是Microsoft一貫使用的Thunk技術(shù)。Microsoft將成員函數(shù)指針分為兩種:

struct pmf_type1{
    void* vcall_addr;
};

struct pmf_type2{
    void* vcall_addr;
    int  delta;  //調(diào)整this指針用
};

vcall_addr是Microsoft?的Thunk技術(shù)核心所在。vcall_addr是一個(gè)指針,隱藏了它所指的函數(shù)是虛擬函數(shù)還是普通函數(shù)的區(qū)別。如果所指的成員函數(shù)是一個(gè)普通成員函數(shù),vcall_addr是成員函數(shù)的函數(shù)地址。如果所指的成員函數(shù)是虛成員函數(shù),那么vcall_addr指向一小段代碼,這段代碼會根據(jù)this指針和虛函數(shù)索引值尋找出真正的函數(shù)地址,然后跳轉(zhuǎn)到真實(shí)的函數(shù)地址處執(zhí)行。
Microsoft根據(jù)情況選用函數(shù)指針結(jié)構(gòu)表示成員函數(shù)指針,使用Thunk技術(shù)(vcall_addr)實(shí)現(xiàn)虛擬函數(shù)/非虛擬函數(shù)的自適應(yīng),在必要的時(shí)候進(jìn)行this指針調(diào)整(使用delta)。
GCC對于成員函數(shù)指針統(tǒng)一使用下面的結(jié)構(gòu)進(jìn)行表示:

struct        
{    
    void* __pfn;  //函數(shù)地址,或者是虛擬函數(shù)的index    
    long __delta; // offset, 用來進(jìn)行this指針調(diào)整   
};

不管是普通成員函數(shù),還是虛成員函數(shù),信息都記錄在__pfn。一般來說因?yàn)閷R的關(guān)系,函數(shù)地址都至少是4字節(jié)對齊的。即函數(shù)地址的最低位兩個(gè)bit總是0。?GCC充分利用了這兩個(gè)bit。如果是普通的函數(shù),__pfn記錄函數(shù)的真實(shí)地址,最低位兩個(gè)bit就是全0,如果是虛成員函數(shù),最后兩個(gè)bit不是0,剩下的30bit就是虛成員函數(shù)在函數(shù)表中的索引值。
GCC先取出函數(shù)地址最低位兩個(gè)bit看看是不是0,若是0就使用地址直接進(jìn)行函數(shù)調(diào)用。若不是0,就取出前面30位包含的虛函數(shù)索引,通過計(jì)算得到真正的函數(shù)地址,再進(jìn)行函數(shù)調(diào)用。
GCC和Microsoft對成員函數(shù)指針實(shí)現(xiàn)最大的不同就是GCC總是動(dòng)態(tài)計(jì)算出函數(shù)地址,而且每次調(diào)用都要判斷是否為虛函數(shù),開銷自然要比Microsoft的實(shí)現(xiàn)要大一些。
在this指針調(diào)整方面,GCC和Mircrosoft的做法是一樣的。不過GCC在任何情況下都會帶上__delta變量,如果不需要調(diào)整,__delta=0
GCC的實(shí)現(xiàn)比Microsoft簡單,在所有場合其實(shí)現(xiàn)方式都是一樣的。

4、C++成員函數(shù)指針的限制

C++語言的規(guī)定,基類的成員函數(shù)指針可以賦值給派生類的成員函數(shù)指針,不允許繼承類的成員函數(shù)指針賦值給基類成員函數(shù)指針。
?C++規(guī)定編譯器必須提供一個(gè)從基類成員函數(shù)指針到繼承類成員函數(shù)指針的默認(rèn)轉(zhuǎn)換。C++編譯器提供的默認(rèn)轉(zhuǎn)換最關(guān)鍵的就是this指針調(diào)整。
因此,一般情況下不要將繼承類的成員函數(shù)指針賦值給基類成員函數(shù)指針。不同C++編譯器可能有不同的表現(xiàn)。
解決方案:
A、不要使用static_cast將繼承類的成員函數(shù)指針賦值給基類成員函數(shù)指針,如果一定要使用,首先確定沒有問題。
B、如果一定要使用static_cast,注意不要使用多繼承。
C、如果一定要使用多繼承的話,不要把一個(gè)基類的成員函數(shù)指針賦值給另一個(gè)基類的函數(shù)指針。
D、單繼承要么全部不使用虛函數(shù),要么全部使用虛函數(shù)。不要使用非虛基類,卻讓子類包含虛函數(shù)。

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
    }
    void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    double sum()
    {
        cout << "Parent::" << __func__<< endl;
        double ret = m_i + m_j;
        cout <<ret << endl;
        return ret;
    }
    void display()
    {
        cout << "Parent::display()" << endl;
    }
    int add(int value)
    {
        return m_i + m_j + value;
    }
protected:
    int m_i;
    int m_j;
};

class ChildA : public Parent
{
public:
    ChildA(int i, int j, double d):Parent(i, j)
    {
        m_d = d;
    }
    void print()
    {
        cout << "ChildA::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
        cout << "m_d = "<< m_d << endl;
    }
    double sum()
    {
        cout << "ChildA::" << __func__<< endl;
        double ret = m_i + m_j + m_d;
        cout << ret << endl;
        return ret;
    }
private:
    void display()
    {
        cout << "ChildA::display()" << endl;
    }
private:
    double m_d;
};

int main(int argc, char *argv[])
{
    Parent parent(100,200);
    ChildA childA(1,2,3.14);
    Parent* pTestA = &childA;
    typedef void (Parent::*pPrintFunc)();
    pPrintFunc pPrint = &Parent::print;
    typedef double (Parent::*pSumFunc)();
    pSumFunc pSum = &Parent::sum;
    typedef void (Parent::*pDisplayFunc)();
    pDisplayFunc pDisplay = &Parent::display;

    printf("0x%X\n",pPrint);
    printf("0x%X\n",pSum);
    printf("0x%X\n",pDisplay);
    //不能將派生類的成員函數(shù)指針賦值給基類的函數(shù)指針
    //pPrint = &ChildA::print;//error
    //可以將基類的成員函數(shù)指針賦值給派生類
    void (ChildA::*pChildPrintFunc)() = pPrint;
    (childA.*pChildPrintFunc)();//Parent::print
    void (*p)() = reinterpret_cast<void (*)()>(pPrint);
    p();

    return 0;
}

5、靜態(tài)成員函數(shù)指針

對于靜態(tài)成員函數(shù),函數(shù)體內(nèi)部沒有this指針,與類的其它成員函數(shù)共享類的命名空間,但靜態(tài)成員函數(shù)并不是類的一部分,靜態(tài)成員函數(shù)與常規(guī)的全局函數(shù)一樣,成員函數(shù)指針的語法針對靜態(tài)成員函數(shù)并不成立。
靜態(tài)成員函數(shù)的函數(shù)指針定義語法如下:

ReturnType (* pointerName) (ArgumentLList);
ReturnType:成員函數(shù)返回類型
Argument_List: 成員函數(shù)參數(shù)列表
pointerName:指針名稱

靜態(tài)成員函數(shù)的函數(shù)指針的使用與全局函數(shù)相同,但靜態(tài)成員函數(shù)指針保存的仍舊是個(gè)相對地址。

#include <iostream>

using namespace std;

class Test
{
public:
    static void print()
    {
        cout << "Test::print" << endl;
    }
};

int main(int argc, char *argv[])
{
    void (* pFunc)() = &Test::print;
    cout << pFunc << endl;//1
    //直接調(diào)用
    pFunc();//Test::print
    (*pFunc)();//Test::print
    Test test;
    //(test.*pFunc)();//error
    Test* pTest = &test;
    //(pTest->*pFunc)();//error

    return 0;
}

6、普通成員函數(shù)指針

非靜態(tài)、非虛的普通成員函數(shù)指針不能直接調(diào)用,必須綁定一個(gè)類對象。
普通函數(shù)指針的值指向代碼區(qū)中的函數(shù)地址。如果強(qiáng)制轉(zhuǎn)換為普通函數(shù)指針后調(diào)用,成員函數(shù)內(nèi)部this指針訪問的成員變量將是垃圾值。

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
    }
    void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    double sum()
    {
        cout << "Parent::" << __func__<< endl;
        double ret = m_i + m_j;
        cout <<ret << endl;
        return ret;
    }
    void display()
    {
        cout << "Parent::display()" << endl;
    }
    int add(int value)
    {
        return m_i + m_j + value;
    }
protected:
    int m_i;
    int m_j;
};

class ChildA : public Parent
{
public:
    ChildA(int i, int j, double d):Parent(i, j)
    {
        m_d = d;
    }
    void print()
    {
        cout << "ChildA::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
        cout << "m_d = "<< m_d << endl;
    }
    double sum()
    {
        cout << "ChildA::" << __func__<< endl;
        double ret = m_i + m_j + m_d;
        cout << ret << endl;
        return ret;
    }
private:
    void display()
    {
        cout << "ChildA::display()" << endl;
    }
private:
    double m_d;
};

int main(int argc, char *argv[])
{
    Parent parent(100,200);
    ChildA childA(1,2,3.14);
    Parent* pTestA = &childA;
    typedef void (Parent::*pPrintFunc)();
    pPrintFunc pPrint = &Parent::print;
    typedef double (Parent::*pSumFunc)();
    pSumFunc pSum = &Parent::sum;
    typedef void (Parent::*pDisplayFunc)();
    pDisplayFunc pDisplay = &Parent::display;

    printf("0x%X\n",pPrint);
    printf("0x%X\n",pSum);
    printf("0x%X\n",pDisplay);
    //綁定類對象進(jìn)行調(diào)用
    (pTestA->*pPrint)();
    (pTestA->*pSum)();
    (pTestA->*pDisplay)();
    //強(qiáng)制轉(zhuǎn)換為普通函數(shù)指針
    void (*p)() = reinterpret_cast<void (*)()>(pPrint);
    p();//打印隨機(jī)值

    return 0;
}

7、虛成員函數(shù)指針

C++通過虛函數(shù)提供了運(yùn)行時(shí)多態(tài)特性,編譯器通常使用虛函數(shù)表實(shí)現(xiàn)虛函數(shù)。

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
    }
    virtual void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    virtual double sum()
    {
        cout << "Parent::" << __func__<< endl;
        double ret = m_i + m_j;
        cout <<ret << endl;
        return ret;
    }
    virtual void display()
    {
        cout << "Parent::display()" << endl;
    }
    int add(int value)
    {
        return m_i + m_j + value;
    }
protected:
    int m_i;
    int m_j;
};

class ChildA : public Parent
{
public:
    ChildA(int i, int j, double d):Parent(i, j)
    {
        m_d = d;
    }
    virtual void print()
    {
        cout << "ChildA::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
        cout << "m_d = "<< m_d << endl;
    }
    virtual double sum()
    {
        cout << "ChildA::" << __func__<< endl;
        double ret = m_i + m_j + m_d;
        cout << ret << endl;
        return ret;
    }
private:
    void display()
    {
        cout << "ChildA::display()" << endl;
    }
private:
    double m_d;
};

class ChildB : public Parent
{
public:
    ChildB(int i, int j, double d):Parent(i, j)
    {
        m_d = d;
    }
    virtual void print()
    {
        cout << "ChildB::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
        cout << "m_d = "<< m_d << endl;
    }
    virtual double sum()
    {
        cout << "ChildB::" << __func__<< endl;
        double ret = m_i + m_j + m_d;
        cout << ret << endl;
        return ret;
    }
private:
    void display()
    {
        cout << "ChildB::display()" << endl;
    }
private:
    double m_d;
};

int main(int argc, char *argv[])
{
    Parent parent(100,200);
    ChildA childA(1,2,3.14);
    //childA.display();//error,編譯時(shí)private不可訪問
    ChildB childB(100,200,3.14);
    Parent* pTestA = &childA;
    Parent* pTestB = &childB;
    typedef void (Parent::*pVPrintFunc)();
    pVPrintFunc pPrint = &Parent::print;

    (parent.*pPrint)();//Parent::print
    (pTestA->*pPrint)();//ChildA::print,多態(tài)
    (pTestB->*pPrint)();//ChildB::print,多態(tài)

    typedef double (Parent::*pVSumFunc)();
    pVSumFunc pSum = &Parent::sum;

    (parent.*pSum)();//Parent::sum
    (pTestA->*pSum)();//ChildA::sum,多態(tài)
    (pTestB->*pSum)();//ChildB::sum,多態(tài)

    typedef void (Parent::*pVDisplayFunc)();
    pVDisplayFunc pDisplay = &Parent::display;

    (parent.*pDisplay)();//Parent::display
    (pTestA->*pDisplay)();//ChildA::display,多態(tài)
    (pTestB->*pDisplay)();//ChildB::display,多態(tài)

    printf("0x%X\n",pPrint);
    printf("0x%X\n",pSum);
    printf("0x%X\n",pDisplay);

    return 0;
}

虛成員函數(shù)指針的值是一個(gè)相對地址,表示虛函數(shù)在虛函數(shù)表中,離表頭的偏移量+1。
當(dāng)一個(gè)對象調(diào)用虛函數(shù)時(shí),首先通過獲取指向虛函數(shù)表指針的值得到虛函數(shù)表的地址,然后將虛函數(shù)表的地址加上虛函數(shù)離表頭的偏移量即為虛函數(shù)的地址。?

8、成員函數(shù)指針示例

成員函數(shù)指針的一個(gè)重要應(yīng)用是根據(jù)輸入來生成響應(yīng)事件,使用不同的處理函數(shù)來處理不同的輸入。

#include <stdio.h>
#include <iostream>
#include <string.h>

using namespace std;

//虛擬打印機(jī)
class Printer {
public:
    //復(fù)制文件
    void Copy(char *buff, const char *source)
    {
        strcpy(buff, source);
    }
    //追加文件
    void Append(char *buff, const char *source)
    {
        strcat(buff, source);
    }
};

//菜單中兩個(gè)可供選擇的命令
enum OPTIONS { COPY, APPEND };

//成員函數(shù)指針
typedef void(Printer::*PTR) (char*, const char*);

void working(OPTIONS option, Printer *machine,
             char *buff, const char *infostr)
{
    // 指針數(shù)組
    PTR pmf[2] = { &Printer::Copy, &Printer::Append };
    switch (option)
    {
    case COPY:
        (machine->*pmf[COPY])(buff, infostr);
        break;
    case APPEND:
        (machine->*pmf[APPEND])(buff, infostr);
        break;
    }
}

int main() {
    OPTIONS option;
    Printer machine;
    char buff[40];

    working(COPY, &machine, buff, "Strings ");
    working(APPEND, &machine, buff, "are concatenated!");

    std::cout << buff << std::endl;
}

// Output:
// Strings are concatenated!

三、C++類成員函數(shù)的調(diào)用分析

1、成員函數(shù)調(diào)用簡介

類中的成員函數(shù)存在于代碼段。調(diào)用成員函數(shù)時(shí),類對象的地址作為參數(shù)隱式傳遞給成員函數(shù),成員函數(shù)通過對象地址隱式訪問成員變量,C++語法隱藏了對象地址的傳遞過程。由于類成員函數(shù)內(nèi)部有一個(gè)this指針,類成員函數(shù)的this指針會被調(diào)用的類對象地址賦值。因此,如果類成員函數(shù)中沒有使用this指針訪問成員,則類指針為NULL時(shí)仍然可以成功對該成員函數(shù)進(jìn)行調(diào)用。static成員函數(shù)作為一種特殊的成員函數(shù),函數(shù)內(nèi)部不存在this指針,因此類指針為NULL時(shí)同樣可以成功對靜態(tài)成員函數(shù)進(jìn)行調(diào)用。

#include <iostream>
#include <string>

using namespace std;

namespace Core {

class Test
{
public:
    Test(int i)
    {
        this->i = i;
    }
    void print()
    {
        cout << "i = " << i << endl;
    }
    void sayHello()
    {
        cout << "Hello,Test." << endl;
    }
    static void printHello()
    {
        cout << "Hello,Test." << endl;
    }
private:
    int i;
};

}

int main()
{
    using namespace Core;
    Core::Test* ptest = NULL;
    ptest->sayHello();//Hello,Test.
    ptest->printHello();
    //定義函數(shù)指針類型
    typedef void (Test::*pFunc)();
    //獲取類的成員函數(shù)地址
    pFunc p = &Test::print;
    Test test(100);
    //調(diào)用成員函數(shù)
    (test.*p)();//i = 100

    return 0;
}

2、普通成員函數(shù)調(diào)用機(jī)制分析

普通成員函數(shù)通過函數(shù)地址直接調(diào)用。

#include <iostream>

using namespace std;

class Test
{
public:
    void print()
    {
        cout << "Test::print" << endl;
    }
};

int main()
{
    Test test;
    Test* p = &test;
    p->print();
}

對于非虛、非靜態(tài)成員函數(shù)的調(diào)用,如p->print(),C++編譯器會生成如下代碼:

Test* const this = p;
void Test::print(Test* const this)
{
    cout << "Test::print" << endl;
}

不管指針p是任何值,包括NULL,函數(shù)Test::print()都可以被調(diào)用,p被作為this指針并當(dāng)作參數(shù)傳遞給print函數(shù)。因此,當(dāng)傳入print函數(shù)體內(nèi)的p指針為NULL時(shí),只要不對p指針進(jìn)行解引用,函數(shù)就能正常調(diào)用而不發(fā)生異常退出。

#include <iostream>

using namespace std;

class Test
{
public:
    void print()
    {
        cout << "Test::print" << endl;
        sayHello();
    }
    void sayHello()
    {
        cout << "Test::sayHello" << endl;
    }
};

int main()
{
    Test* p = NULL;
    p->print();
}

// output:
// Test::print
// Test::sayHello

3、靜態(tài)成員函數(shù)調(diào)用機(jī)制分析

靜態(tài)成員函數(shù)通過函數(shù)地址進(jìn)行調(diào)用,其調(diào)用方式同全局函數(shù)。

4、虛成員函數(shù)調(diào)用機(jī)制分析

虛成員函數(shù)的調(diào)用涉及運(yùn)行時(shí)多態(tài)。
當(dāng)一個(gè)對象調(diào)用虛函數(shù)時(shí),首先通過運(yùn)行時(shí)對象獲取指向虛函數(shù)表指針的值得到虛函數(shù)表的地址,然后將虛函數(shù)表的地址加上虛函數(shù)離表頭的偏移量即為虛函數(shù)的地址。?基類對象內(nèi)部的虛函數(shù)表指針指向基類的虛函數(shù)表,派生類對象的虛函數(shù)表指針指向派生類的虛函數(shù)表,確保運(yùn)行時(shí)對象調(diào)用正確的虛函數(shù)。

文章題目:C++語言學(xué)習(xí)(十四)——C++類成員函數(shù)調(diào)用分析
當(dāng)前路徑:http://bm7419.com/article34/jccpse.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、微信小程序、網(wǎng)站排名靜態(tài)網(wǎng)站、服務(wù)器托管虛擬主機(jī)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都app開發(fā)公司