c++虚函数实现机制
c++多态指的是基类指针调用子类的方法. 多态的实现用的虚函数实现的,而虚函数是利用了虚函数表(vtable)实现的 。在每个类的实例化对象存在__vfptr指针指向了虚函数表,里面存在了该类所有的实际执行的虚函数的地址。
多态类对象中虚函数和虚函数表
class A
{
public:
A(){}
void Test(){}
virtual void Print1(){}
virtual void Print2(){}
};
class B : public A
{
public:
B(){}
virtual void Print2() override{}
};
class C
{
public:
C() {}
void Print1() {}
};
int main()
{
A* a = new B();
A* aa = new A();
A* c = new B();
C* d = new C();
printf("A Print1 %p\n", &A::Print1);
printf("A Print2 %p\n", &A::Print2);
printf("B Print1 %p\n", &B::Print1);
printf("B Print2 %p\n", &B::Print2);
system("pause");
return 0;
}
这里可以清晰看到ClassA和ClassB的虚函数地址区别,当然这里仅仅是类函数指针,多态真正的实现是Runtime,在类对象上体现。上面由class B创建的基类指针对象a的__vfptr分别指向Class A的Print1和Class B的Print2. 而由Class A创建的对象aa的__vfptr分别指向Class A的Print1和Print2.
从上面可以看出:
(1)类函数指针和对象函数指针不是同一个东西,两者地址不同的
(2)同一个类的所有实例化对象的虚函数表是同一个
(3)没有虚函数的类对象并没有虚函数表
虚函数表在类对象中的位置和虚函数表的妙用
在类对象的内存地址中, 多态类对象分配的内存首地址存着虚函数指针__vfptr,__vfptr指向虚函数表(目前探查是个数组)
class A
{
public:
A(){}
void Test(){}
virtual void Print1() { printf("class A Print1\n"); }
virtual void Print2() { printf("class A Print2\n"); }
};
class B : public A
{
public:
B(){}
virtual void Print2() override{ printf("class B Print2\n"); }
};
class C : public A
{
public:
C() {}
virtual void Print1() { printf("class C Print1\n"); }
virtual void Print2() { printf("class C Print2\n"); }
};
using myfunc = void(*)(void);
class D : public C
{
public:
D(){}
void Print1() {}
};
int main()
{
A* a = new B();
int* p = (int*)a;
printf("object adress is%p \n", p);
int* vptr = (int*)p[0];
printf("vfptr is %p \n", (int*)vptr);
myfunc myptr1 = (myfunc)(vptr[0]);
myptr1();
myfunc myptr2 = (myfunc)(vptr[1]);
myptr2();
// runtime-change vfptr
A* b = new C();
int* q = (int*)b;
p[0] = q[0];
a->Print1();
a->Print2();
system("pause");
return 0;
}
__vfptr更改之前
__vfptr更改之后
可以看到通过多态类对象指针首个元素就是__vfptr虚函数表指针,我们可以在运行时显示通过虚函数指针访问虚函数和以及改变虚函数表的地址,或者改变虚函数表存储的地址
RTTI
RTTI(run-time type identification), 运行时类型标识. 可以在Runtime拿到各种数据的一些简单类型信息, 如名字等等.
RTTI在c++的两个应用为 typeid 和 dynamic_cast
typeid
int main()
{
A* a = new B();
A* aa = new A();
A* c = new B();
C* d = new C();
int e = 1;
cout << typeid(a).name() << endl;
cout << typeid(aa).name() << endl;
cout << typeid(d).name() << endl;
cout << typeid(e).name() << endl;
system("pause");
return 0;
}
可以看出typeid类型是由目前的赋值对象类型决定,而非创建的对象。
dynamic_cast
class A
{
public:
A(){}
void Test(){}
virtual void Print1(){}
virtual void Print2(){}
};
class B : public A
{
public:
B(){}
virtual void Print2() override{}
};
class C
{
public:
C() {}
void Print1() {}
};
class D : public C
{
public:
D(){}
void Print1() {}
};
int main()
{
A* a = new B();
B* b = dynamic_cast<B*>(a);
if (b != nullptr)
{
printf("b is not null");
}
C* c = new D();
D* d = dynamic_cast<D*>(c);
if (d != nullptr)
{
printf("d is not null");
}
system("pause");
return 0;
}
可以看到在编译期没有虚函数表(多态类型)的不能进行dynamic_cast