C++函数指针与虚函数指针对私有虚函数进行访问
虚函数表与虚函数指针
(图片来自侯捷老师)
- 虚函数表的首地址即为类对象的起始地址存放的指针,该指针即为虚函数表指针,指向虚函数表
- 虚函数表中每一项都是一个指向虚函数地址的指针,非虚函数不在表中
函数指针
它其实就是一个特殊的指针,它用于指向函数被加载到的内存首地址,可用于实现函数调用。听上有点像函数名,函数名也是记录了函数在内存中的首地址,加()就可以调用。
函数指针就是一种特殊的指针。
如果你要声明一个变量:
int a ;
而一个指针呢:
int *a;
那一个函数指针,就是在一个变量指针的写法基础上加一个括号,告诉他这是一个指向函数的指针就可以:
int (*a)();
这样,a就是一个函数指针了。
这个括号(*a)一定要加,否则就成了int *a();编译器会认为这是一个 返回int *的函数a;
这时候呢,int (*a)();就声明了一个函数指针变量a,它可以指向一个返回int,参数列表为空的函数。
前面的int,就是这个函数指针的返回值,a是变量名,最后一个()是参数列表。
int function() // 正确的函数声明
{
return 0;
}
int (*a)() // 错误:这是一个变量,不能当函数一样定义
{
return 0;
}
//你只能这样:
int (*a)(); //声明一个函数指针变量a,
int main()
{
a = function; //给函数指针赋值。
a(); //通过函数指针调用
// 也可以直接把声明和赋值写在一起:这就像是 int i;和int * p = i;的区别
int (*b)() = function;
b();
return 0;
}
//函数定义:
#include <iostream>
using namespace std;
typedef void (*f_ptr)();
void aaa()
{
cout << "aaa" << endl ;
}
// void (* f())()
f_ptr f() //返回值是函数指针的函数定义, 语义一目了然
{
return aaa;
}
int main()
{
// void (*(*f_ptr)())() = f;
// f_ptr()();
f_ptr (*ff)() = f; //返回函数指针的函数指针
ff()();
return 0;
}
通过虚函数表指针访问虚函数
int* vptr = (int*)*(int *)(&student);
上面这句意思是:
- 取对象地址&student
- 强转(int*)类型,此时是对象的第一个位置的值,也就是指向虚函数表指针的地址
- *(int *)(&student),对指针解引用,就是虚函数表指针
- (int* )* (int* )(&student),再次强转,此时指针指向表中第一个元素,该元素就是第一个类中的虚函数的地址
- 若要使用该虚函数,则再次强转为函数指针就可以直接使用
#include "pch.h"
#include <iostream>
using namespace std;
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
};
int main()
{
typedef void(*Fun)(void);
Base b;
Fun pFun = NULL;
Base * p = &b;
cout << "该对象的地址:" << p << endl;
cout << "虚函数表的指针也是从这个地址"<< (int*)(&b) <<"开始存的" << endl << endl;
cout << "虚函数表的指针指向的地址10进制:" << *(int*)(&b) << "即虚函数表的指针存的内容"<<endl;
cout << "即虚函数表的地址:" << (int*)*(int*)(&b) << endl << endl;
pFun = (Fun)*(int*)*(int*)(&b);//第一个虚函数的指针
cout << "第一个虚函数的地址:" << pFun << endl;
pFun();
Fun gFun = NULL;
gFun = (Fun)*((int*)*(int*)(&b) + 1);//第二个虚函数的指针
Fun hFun = NULL;
hFun = (Fun)*((int*)*(int*)(&b) + 2);//第三个虚函数的指针
}
输出结果:
另一个例子
#include<iostream>
#include<stdio.h>
using namespace std;
class A {
public:
virtual void func1() {
cout << "is A func1" << endl;
}
virtual void func2() {
cout << "is A func2" << endl;
}
};
class B: public A{
public:
virtual void func1() {
cout << "is B func1" << endl;
}
virtual void func2() {
cout << "is B func2" << endl;
}
};
typedef void (*Func)();
int main() {
A* a = new B();
Func f = nullptr;
//f = (Func)*((int*)*(int*)(a));// is B func1
f = (Func)*((int*)*(int*)(a)+1);// is B func2
f();
}