虚函数与虚函数指针

虚函数

虚函数的作用是什么?

主要是为了实现多态机制,是指在继承层次中基类的指针可以具有多种不同指向,即当基类指针指向某个派生类对象时,通过基类指针可以调用到派生类的函数,而不是基类的函数。

代码演示
class Base {     virtual void print(void);    }
class Drive1 :public Base{    virtual void print(void);    }
class Drive2 :public Base{    virtual void print(void);    }
Base * ptr1 = new Base; 
Base * ptr2 = new Drive1;  
Base * ptr3 = new Drive2;
ptr1->print(); //调用Base::print()
prt2->print();//调用Drive1::print()
prt3->print();//调用Drive2::print()

这是一种运行时多态,基类指针只有在运行时,才知道指向的对象是什么,这种运行时多态是通过虚函数表实现的。

虚函数指针

什么是虚函数指针

当一个类本身定义了虚函数,或者基类有虚函数时,为了支持多态机制,编译器会为该类添加一个虚函数指针vptr。虚函数指针一般放在对象内存布局的第一个位置,对象的地址就是虚函数指针的地址。

代码演示
class Base
{
public:
	Base(int i) :baseI(i) {};
	virtual void print(void) { cout << "调用了第一个虚函数Base::print()"<<endl; }
	virtual void setI() { cout << "调用了第二个虚函数Base::setI()"<<endl; }
	virtual ~Base(){  }
private:
	int baseI;
};
int main() {
	Base b(1000);
	int* vptrAdree = (int*)(&b);
	typedef void(*Fun)(void);
	cout << "----------------------------------------------" << endl;
	cout << "对象b的地址" << &b << endl;
	cout << "虚函数指针vptr的地址" << (int*)(&b) << endl;
	cout << "虚函数vptr加上解引用*结果是虚函数表的地址" << *(int*)(&b) << endl;
	cout << "虚函数表的地址转为int类型" << (int*)*(int*)(&b) << endl;
	cout << "虚函数表的int类型的地址再转为FUN函数指针类型" << (Fun)*(int*)*(int*)(&b) << endl;
	Fun vfunc = (Fun) * (int*)*(int*)(&b);
	vfunc();
	cout << "虚函数表中第二个虚函数的地址为" << (Fun) * ((int*)*(int*)(&b) + 1) << endl;
	vfunc = (Fun) * ((int*)*(int*)(&b) + 1);
	vfunc();
}
----------------------------------------------
对象b的地址010FFA94
虚函数指针vptr的地址010FFA94
虚函数vptr加上解引用*结果是虚函数表的地址432948
虚函数表的地址转为int类型00069B34
虚函数表的int类型的地址再转为FUN函数指针类型00061299
调用了第一个虚函数Base::print()
虚函数表中第二个虚函数的地址为000611E0
调用了第二个虚函数Base::setI()

虚函数指针的地址与对象的地址是一致的,虚函数指针指向的内容就是虚函数表的地址(通过加解引用运算法*得到),再将虚函数表的地址转换为int类型,方便以后移动指向虚函数表中的slot。虚函数表中的slot储存的就是每个虚函数的地址,在此基础上增加地址就可以指向不同的slot,所以将对应的地址转化为函数指针的形式,就能通过赋值给函数指针变量来调用对应虚函数。

####typedef void(*Fun)(void);

代码中涉及到这样一句代码,这里需要解释一下。

typedef的用法
  1. 最常见的用法
int i;
typedef int myint;
myint j;

使用myint来替代关键字int,以后使用myint的位置相对于在用int

  1. 函数指针

方法1:函数指针的形式是返回类型(*函数名)(参数表)

char (*pFun)(int);//定义一个函数指针,他指向一个返回值类型为char,参数为一个int类型的函数,pFun为一个函数指针

方法2:typedef可以让函数指针更加直观typedef 返回类型(*新类型)(参数表)

typedef char(*PTRFUN)(int);//定义了一种PTRFUN类型,这种类型是某种指向函数的指针,这种函数以int为参数返回值为char
PTRFUN pFun;//使用PTRFUN定义变量pFun以后就可以像方法1一样正常使用这个变量
char gFun(int a){return;}
int main(){
    pFun=gfun;
    (*pfun)(2);
}

所以typedef void(*Fun)(void);表示定义了一种Fun类型,这种类型指向返回值为void参数为void的函数,可以使用Fun定义函数指针变量。

总结

编译器在编译的时候,发现基类中有虚函数,此时编译器会为每个包含虚函数的类创建一个虚表,该表是一个存放每个虚函数地址的一维数组。由于基类和派生类都有虚函数,所以编译器为这两个类都建立一个虚表。

派生类调用虚函数时如何找到正确的函数

编译器为每个带有虚函数的类的对象自动创建一个虚表指针即vptr,这个指针指向了对象所属类的虚表。在程序运行时,根据对象的类型去初始化vptr,从而让vptr正确的指向所属类的虚表,所以在调用虚函数的时候能找到正确的函数。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值