C++之成员函数指针

前面介绍过基本的函数指针,这里介绍类的成员函数指针。

弄清楚一般的函数指针,对于类的成员函数指针也不会那么陌生不懂,毕竟他们都是函数指针,只不过因为多了一层类,又有所变化,下面以一个实例来介绍。

#include <iostream>
 
 
using namespace std;
 
 
class Person {
    public:
        void sayHello(){
            cout<<"你好"<<"  ";
            printf("%d\n",&Person::sayHello);
        }
        virtual void sayName(){
            cout<<"我没有名字"<<"  ";
            printf("%d\n",&Person::sayName);
        }
};
 
 
class Child : public Person {
    public:
    void sayHello(){
        cout<<"你好"<<"  ";
        printf("%d\n",&Child::sayHello);
    }
    virtual void sayName(){
        cout<<"我是小明"<<"  ";
        printf("%d\n",&Child::sayName);
    }
};
 
 
//typedef void (*FunctionPointer)();
typedef void (Person::*PersonMemberFunctionPointer)();
typedef void (Child::*ChildMemberFunctionPointer)();
 
 
void runfuncName(Person * obj, void (Person::*func)() ){//PersonMemberFunctionPointer func
    (obj ->* func)();
}
 
 
 
 
int main(int argc, char *argv[])
{
    Person someone;
    Child xiaoming;
    PersonMemberFunctionPointer pp;
    pp = &Person::sayHello;
    (someone .* pp)();
    //等价于 (&someone ->* pp)();
    //也等价于 someone.sayHello();
    (xiaoming.*pp)();
    //pp=&Child::sayHello;(不能运行,需要强制转换)
    ChildMemberFunctionPointer cp = &Child::sayHello;
    (xiaoming.*cp)();
 
 
    runfuncName(&xiaoming,pp);
 
 
    PersonMemberFunctionPointer pp2 = &Person::sayName;
    (someone.*pp2)();
    (xiaoming.*pp2)();//必须是公开继承,才有权限
 
 
    //pp2 = &Child::sayName;(不能运行,需要强制转换)
 
 
 
 
 
 
    return 0;
}

这里定义了两个类,父类Person和子类Child,为公有继承,每个类都有一个同名函数sayHello()和一个同名虚函数sayName(),typedef定义了两个新的函数指针类型,为Person类的函数指针,和Child类的函数指针,后面一共有4个输出。

可以看出类的函数指针和前面介绍的函数指针的区别就是在定义时指针前面需要加上类名和域作用符,即“类名::”的格式,总的格式为函数返回值类型(类名::*函数指针名)(参数列表参数类型),而且调用指针内容时非静态函数需要通过对应类的对象来调用,静态函数需要通过类名来调用,具体格式有几种,一般的函数的存储地址固定,所以根据其名称可以直接得到具体地址,类的成员函数地址同样固定,和类绑定,但是需要通过对象访问,因为通过对象访问才能传入该对象的this指针,才能确定成员变量,所以我们通过类名、域作用符和函数名称得到成员函数是的地址仍然要通过对象访问。

运用函数指针一共调用了6次类的成员函数,其中前4次调用的是sayHello()函数,最后2次调用的是sayName()函数,而前4次中第4次又有所不同。


可以看到前4次输出结果中除了第3次是4224428,其余3次均是4224596,第1次4224596是因为传入父类对象调用父类sayHello()成员函数,而第2次是通过传入子类的实例化对象到父类sayHello()的函数来访问,结果仍然是调用了父类函数,按照道理来说,我们会想到应该是子类的sayHello()函数调用才对,但是实际上,子类虽然有同名的函数,但只是将父类的函数给屏蔽了,并不是存储上的覆盖,仍然是访问的是父类的sayHello()函数,第3次是通过传入子类的实例化对象调用子类的指向sayHello()的函数指针来访问,所以是调用的子类的sayHello()函数,与第1次类似,而第4次是通过一个函数来访问的,函数的参数列表的数据类型都是父类有关的类型,为父类对象的指针和父类的成员函数指针,而传入的参数是子类的对象的地址和父类的成员函数的相对地址,根据动态绑定,应当访问的是子类对象,但是同第2次,所以访问的仍然是父类的sayHello()函数,而最后2次调用sayName()成员函数,对象中存储虚函数表的首地址,根据这个地址访问的也只是虚函数表,子类虚函数与父类虚函数同名,子类虚函数表中子类的虚函数覆盖了父类同名虚函数的位置,所以通过父类和子类对象以及父类虚函数地址访问的分别是父类的虚函数和子类的虚函数,但是在虚函数表 中相对位置相同,所以都为1

猜你喜欢

转载自blog.csdn.net/hhm853610070/article/details/72886814
今日推荐