文章目录
一、虚函数表
当改类中存在
虚函数
或其基类
含有虚函数
,编译器将会添加一个虚函数表vptr
;
1、简介
只要在类中定义了
virtual
函数当创建对象时,编译器会自动给对象添加一个vptr指针,指向虚函数表;
- 大小为4字节;
#include <iostream>
using namespace std;
class A {
A() {}
~A(){}
void func();
int m_a;
};
class B {
B() {}
~B(){}
virtual void func(){};
int m_a;
};
int main() {
cout << "class sizeof(A) no virtual func:" << sizeof(A) << endl;
cout << "class sizeof(B) with virtual func:" << sizeof(B) << endl;
return 0;
}
2、虚函数
一个函数在类中声明时,在头部加上
virtual
即被声明为虚函数;
2.1类中虚函数与vpbl的关系
2.2 编译器是vtbl如何生成以及给定
1、继承开发环境:
为每个可能需要vtbl的对象文件生成一个vtbl拷贝,连接程序后去除拷贝,最后在exe文件或程序库种为每一个vtbl保留一个
实例;
2、启发式算法:
要在一个对象文件生成一个类的vtbl,要求该文件包含该类的第一个非内联、非纯虚拟函数定义;
由于非内联的限制,启发式算法会失败,故尽量避免把虚函数声明为内联函数;
2.3 虚函数与内联的关系
- 虚函数一般不能是内联;
- 由于内联式在编译期间用被调用的函数体本身来代替函数调用的指令;
- 而虚函数式直到运行时才知道要调用哪一个函数;
2.4 运行时类型识别 RTTI
- 能够让我们在运行时找到对象和类的有关信息【typeid】;
- 是被设计为在类的vtbl基础上实现的;
- 是在每个类的vtbl中的占用的额外单元在加上type_info对象的空间;
3、使用多态时如何通过指针找到对应虚函数
当虚继承时,一般说来,
派生类地址
和其虚基类地址
之间的偏移量是不固定
的,因为如果这个派生类
又被进一步继承的话,最终派生类会把共享
的虚基类实例数据放到一个与上一层派生类
不同的偏移量
处;
#include <iostream>
using namespace std;
class Animal{
public:
virtual void do_sm(){
};
virtual void cry(){
};
virtual void fly(){
};
private:
string m_name;
};
class woodpecker : public Animal {
public:
virtual void do_sm() override {
cout << "woodpecker do ...." << endl;
}
virtual void cry() override {
cout << "woodpecker cry ...." << endl;
}
virtual void fly() override {
cout << "woodpecker fly ...." << endl;
}
void peck_wood() {
cout << "woodpecker peck_wood ...." << endl;
}
private:
int m_weight;
};
class chicken : woodpecker {
public:
virtual void do_sm() override {
cout << "chicken do ...." << endl;
}
virtual void cry() override {
cout << "chicken cry ...." << endl;
}
void lay_egg(){
cout << "chicken lay_egg ...." << endl;
}
private:
string m_name;
int m_canLayEggs;
};
int main() {
typedef void(*Func)(void);
woodpecker w;
int* obj = (int*)&w; // 转换指向对象的指针类型
int* vp = (int*)*obj; // 获取虚表指针
Func f1 = (Func)vp[0]; // (*obj->vp[n])(p)
Func f2 = (Func)vp[1];
Func f3 = (Func)vp[2];
f1(); // do_sm
f2(); // cry
f3(); // fly
return 0;
}