C++知识点随笔(七):成员函数指针、模拟虚函数列表

来源:互联网 发布:佳能ip2880s清零软件 编辑:程序博客网 时间:2024/06/11 19:47

成员函数指针

普通的全局函数指针定义:

typedef void (*PFUN)();

普通的函数指针可以指向返回值和参数类型都相同的全局函数,可是成员函数该怎么办呢?如果成员函数是static的,那么可以正常指向,如下实例:

#include <iostream>using namespace std;class CAA{public:    static void ShowStatic(){        cout << "CAA::ShowStatic()" << endl;    }    void Show(){        cout << "CAA::Show()" << endl;    }};void Show(){    cout << "Global::Show()" << endl;}typedef void (*PFUN)();int main(){    PFUN pfun_1 = Show;     //这里不用 & 也可以,因为全局函数的函数名就是他的地址。    (*pfun_1)();    PFUN pfun_2 = CAA::ShowStatic;  //普通的函数指针指向成员函数的时候,这个成员函数必须是 static 的,并且这里也不用 &,因为加上 static 之后就相当于全局函数了。    (*pfun_2)();    system("pause");    return 0;}

我们知道普通的成员函数都有this指针,用来传入对象的地址,因为普通的成员函数虽然是属于类的,不是属于对象的,但是使用的时候必须通过对象调用。而static成员函数就没有了this指针,所以就相当于全局函数,我们可以不经过对象,而是通过类名作用域来调用他了。
可是上面的例子中,如果我们用PFUN去指向类中的非静态成员函数就会报错。所以我们要使用成员函数指针来指向类中的非静态成员函数,实例如下:

#include <iostream>using namespace std;class CAA{public:    static void ShowStatic(){        cout << "CAA::ShowStatic()" << endl;    }    void Show(){        cout << "CAA::Show()" << endl;    }};typedef void (CAA::*PFUN_CAA)();    //定义成员函数指针int main(){    PFUN_CAA pfun_3 = &CAA::Show;    //(*pfun_3)();      // 不能直接调用,因为这个函数指针指向的是普通成员函数,调用还是需要对象的!    CAA aa;    (aa.*pfun_3)();    CAA* a = new CAA;    (a->*pfun_3)();    system("pause");    return 0;}

如上例所示,成员函数指针定义的时候就指向了是属于哪个类的,如果我们将CAA类的成员函数指针指向了CBB类的成员函数,那么就要对其函数地址进行强转才能编译通过,并且如果CBB类的成员函数中用到了成员属性,那么这些属性的类型和定义的顺序都要和CAA中定义的相同!否则就会出错!因为:我们强转为CAA类的函数地址之后,再使用这个函数指针的时候还是要通过CAA的对象使用,那么就会用到CAA对象地址中的成员属性。所以上述两点一定要相同!实例如下:

#include <iostream>using namespace std;class CAA{public:    CAA(int nValue, double dValue){        this->m_nValue = nValue;        this->m_dValue = dValue;    }    void Show(){        cout << "CAA::Show()," << this->m_nValue << endl;    }private:    double m_dValue;    int m_nValue;};class CBB{public:    void Show(){        cout << "CBB::Show(), " << this->m_nValue << ", " << this->m_dValue << endl;    }private:    double m_dValue;    int m_nValue;   //这里如果不是 int 型的就会发生问题,因为 CAA 的对象在调用 CBB 函数的时候将 CAA 的 this 指针传进来了,所以 Show 函数中所有使用的成员属性都必须和 CAA 中的成员相符,否则就会乱了。    //注意:这里的类型相符具体到:成员属性定义的顺序都要一样!否则,此例中如果CBB的这两个成员属性定义的顺序颠倒了,那么输出的结果就错误了。};typedef void (CAA::*PFUN_CAA)();int main(){    PFUN_CAA pFun = (PFUN_CAA)&CBB::Show;   //强转为 PFUN_CAA 类型    CAA* pA = new CAA(65, 12.34);    (pA->*pFun)();    system("pause");    return 0;}

模拟虚函数列表

虚函数列表功能分析:我们知道虚函数列表其实就是一个指针数组,里面存的都是虚函数的地址,普通继承的情况下子类的vptr(指向虚函数列表的指针)都是由父类继承来的,只有虚继承并且父类存在虚函数的情况下,子类才会有一个自己的vptr(这里不明白可以看我的《C++知识点随笔(五):虚继承》一篇)。由于编译的时候虚函数列表就创建了,所以这个指针数组要是 static 的。下面我们通过成员函数指针的知识来模拟虚函数列表的实现:

#include <iostream>using namespace std;class CFather;typedef void (CFather::*PFUN)();    //定义一个父类的成员函数指针class CFather{public:    CFather(){        this->pFun = arr;    //让vptr指向父类的虚函数列表    }    void AA(){        cout << "CFather::AA()" << endl;    }    void BB(){        cout << "CFather::BB()" << endl;    }    void CC(){        cout << "CFather::CC()" << endl;    }public:    static PFUN arr[];    //父类的虚函数列表    PFUN* pFun;     //vptr};PFUN CFather::arr[] = {&CFather::AA, &CFather::BB, &CFather::CC};//静态成员类外初始化class CSon : public CFather{public:    CSon(){        CFather::pFun = this->arr;    }    void AA(){        cout << "CSon::AA()" << endl;    }    void BB(){        cout << "CSon::BB()" << endl;    }public:    static PFUN arr[];    //子类的虚函数列表};PFUN CSon::arr[] = {(PFUN)&CSon::AA, (PFUN)&CSon::BB, &CFather::CC};//子类重写了父类的AA和BB虚函数,所以在子类的虚函数列表中对应位置的函数指针就指向了子类的函数的地址。void ShowFatherVirtualFunction(CFather* pFather)    //打印父类的虚函数{    for (int i = 0; i < 3; ++i)    {        (pFather->*(*(pFather->pFun)++))();    }}int main(){    CFather* pFather1 = new CFather;   //父类的对象    ShowFatherVirtualFunction(pFather1);    cout << "-----------------------------" << endl;    CFather* pFather2 = new CSon;      //父类指针指向子类对象    ShowFatherVirtualFunction(pFather2);    system("pause");    return 0;}

结果:
这里写图片描述

1 0