是看侯捷老师讲解c++对象模型 虚表和虚指针的笔记和程序验证。
先看两张关键的图吧,右边的三个基类和派生类 A,B,C。定义了两个虚函数,两个一般成员函数,以及几个成员变量。
只有在类中有虚函数时,才会有虚指针以及虚表。一般函数和变量是通过静态绑定方式,调用时通过call xxx(函数地址)访问;而虚函数则是通过 对象指针 -> 虚指针- >虚表 -> 对应函数 这个过程来访问,这也是动态绑定方式。
#include <iostream>
#include <type_traits>
class Base {
public:
Base() {
std::cout << "Base \n";
}
~Base() {
std::cout << "~Base \n";
}
virtual void vfunc1() {
std::cout << "vfunc1 \n";
}
virtual void vfunc2() {
std::cout << "vfunc2 \n";
}
void func1() { std::cout << "func1 \n"; }
void func2() { std::cout << "func2 \n"; }
int m_data = 1;
private:
int m_data1;
int m_data2;
};
class Sub : public Base {
public:
Sub() {
std::cout << "Sub \n";
}
~Sub() {
std::cout << "~Sub \n";
}
virtual void vfunc1() {
std::cout << "sub vfunc1 \n";
}
void func2() { std::cout << "sub func2 \n"; }
};
class SubSub : public Sub {
public:
SubSub() {
std::cout << "SubSub \n";
}
~SubSub() {
std::cout << "~SubSub \n";
}
virtual void vfunc1() {
std::cout << " SubSub vfunc1 \n";
}
void func2() { std::cout << "SubSub func2 \n"; }
int m_data = 2;
private:
int m_data1;
};
int main() {
std::cout << "Hello World!\n";
Base* cls = new SubSub();
cls->vfunc1();
cls->func2(); // 静态绑定,会调用这个类型的值。
auto cls1 = new SubSub();
cls1->func2();
std::cout <<( typeid(cls1) == typeid(SubSub) )<<std::endl;
std::cout << "m_data : " << cls->m_data << std::endl;
auto obj = dynamic_cast<SubSub*>(cls);
if (obj) std::cout << "m_data : " << obj->m_data << std::endl;
SubSub s;
Base& cls2 = s;
cls2.vfunc1();
cls2.func2();
// 下面是假设vptr在对象的开头位置,尝试取出它,并对比。发现下面两个对象的vptr 值是一样的,符合预期。
printf("cls1 vptr value : %p \n", *(&((int*)cls1)[0]));
printf("cls2 vptr value : %p \n", *(&((int*)&cls2)[0]));
return 0;
}
验证结果:
Hello World!
Base
Sub
SubSub
SubSub vfunc1
func2
Base
Sub
SubSub
SubSub func2
0
m_data : 1
m_data : 2
Base
Sub
SubSub
SubSub vfunc1
func2
cls1 vptr value : 0xb5d62d18
cls2 vptr value : 0xb5d62d18
~SubSub
~Sub
~Base运行结束。
如果使用多态方式,一般函数的访问取决于用的是什么类型对象访问,