虚函数-静态绑定-动态绑定
- 如果类中定义了虚函数,那么编译阶段,编译器会给这个类类型产生一个唯一的vftable虚函数表,其中主要存储的是RTTI指针和虚函数的地址。程序运行时,每一张虚函数表都会加载到内存的.rodata只读数据区。
- 一个类中定义了虚函数,那么这个类的对象,其运行时,内存中开始部分,会多存储第一个vfptr虚函数指针,指向相应类型的虚函数表vftable。一个类型定义的n个对象,他们的vfptr指向的都是同一个虚函数表。
- 一个类里面虚函数的个数,不影响对象的内存大小(vfptr),只影响虚函数表的大小。
- 如果派生类中的方法,和基类继承来的某个方法,返回值、函数名、参数列表都相同,而且基类的方法是virtual虚函数,那么派生类的这个方法,自动处理成虚函数。重写<=>覆盖。因此,覆盖指的是虚函数表中虚函数地址被新的重写的虚函数覆盖。
- 总而言之,虚函数表指针存在的意义:一个父类指针指向的究竟是什么类的对象?这个动态信息被存储在虚表指针
class A {
public:
virtual void vfunc1();
virtual void vfunc2();
void func1();
void func2();
private:
int m_data1, m_data2;
};
class B : public A {
public:
void vfunc1(); //自动处理成虚函数。
void func1();
private:
int m_data3;
};
class C: public B {
public:
void vfunc2(); //自动处理成虚函数。
void func2();
private:
int m_data1, m_data4;
};
#include<iostream>
#include<typeinfo>
using namespace std;
#if 0
class Base
{
public:
Base(int data = 10) : ma(data) {}
void show() { cout << "Base::show()" << endl; }
void show(int) { cout << "Base:show(int)" << endl; }
protected:
int ma;
};
class Derive : public Base
{
public:
Derive(int data = 20) : Base(data), mb(data) {}
void show() { cout << "Derive::show()" << endl; }
private:
int mb;
};
int main()
{
Derive d(50);
Base* pb = &d;
pb->show(); // 静态(编译时期)的绑定(函数的调用)call Base::show (0851037h)
pb->show(10); // 静态绑定 call Base::show (0851307h)
cout << sizeof(Base) << endl; // 4
cout << sizeof(Derive) << endl; // 8
cout << typeid(pb).name() << endl; // Base*
cout << typeid(*pb).name() << endl; // Base
return 0;
}
#endif
# if 1
class Base
{
public:
Base(int data = 10) : ma(data) {}
virtual void show() { cout << "Base::show()" << endl; }
virtual void show(int) { cout << "Base:show(int)" << endl; }
protected:
int ma;
};
class Derive : public Base
{
public:
Derive(int data = 20) : Base(data), mb(data) {}
/*
如果派生类的方法,和基类继承来的某个方法,返回值、函数名、参数列表都相同,
而且基类的方法是virtual虚函数,那么派生类的这个方法,自动处理成虚函数
重写《=》覆盖
*/
void show() { cout << "Derive::show()" << endl; }
private:
int mb;
};
int main()
{
Derive d(50);
Base* pb = &d;
/*
pb -> Base Base::show 如果发现show是普通函数,就进行静态绑定
pb -> Base Base::show 如果发现show是虚函数,就进行动态绑定
mov eax, dword ptr[pb] // 取pb的vfptr放入eax
mov ecx, dword ptr[eax] // 将vfptr指向的虚函数地址放入ecx
call ecx // 动态(运行时期)的绑定(函数的调用)
*/
pb->show(); // Derive::show()
pb->show(10); // Base:show(int)
cout << sizeof(Base) << endl; //
cout << sizeof(Derive) << endl; //
/*
pb的类型:Base -> 有没有虚函数
如果Base没有虚函数,*pb识别的就是编译时期的类型 *pb <=> Base类型
如果Base有虚函数,*pb识别的就是运行时期的类型 RTTI类型
pb->d(vfptr)->Derive
*/
cout << typeid(pb).name() << endl; // class Base *
cout << typeid(*pb).name() << endl; // class Derive
return 0;
}
#endif
虚函数的权限问题
class Base
{
public:
Base()
{
cout << "call Base()" << endl;
}
virtual void show()
{
cout << "call Base::show()" << endl;
}
};
class Derive : public Base
{
public:
Derive()
{
cout << "call Derive()" << endl;
}
private:
void show()
{
cout << "call Derive::show()" << endl;
}
};
int main()
{
Base* pb1 = new Derive();
pb1->show();
delete pb1; //当父类虚函数是public的,子类虚函数即使是private的,也还是可以被调用。因为权限检查在编译器执行,但函数的动态绑定是代码执行时确定的。
//反之,如果父类虚函数是private的,子类虚函数即使是public的,也不能实现动态绑定,而会因为权限直接编译错误
return 0;
}
标签:函数,show,静态,void,绑定,29,pb,Base
From: https://www.cnblogs.com/sio2zyh/p/17987520