C++ 对象模型
struct Base {
Base() = default;
~Base() = default;
void Func() {}
int a;
int b;
};
int main() {
Base a;
return 0;
}
struct Base {
Base() = default;
virtual ~Base() = default;
void FuncA() {}
virtual void FuncB() {
printf("FuncB\n");
}
int a;
int b;
};
int main() {
Base a;
return 0;
}
这个含有虚函数的结构体大小为16,在对象的头部,前8个字节是虚函数表的指针,指向虚函数的相应函数指针地址,a占4个字节,b占4个字节,总大小为16。
offset_to_top(0)
:表示当前这个虚函数表地址距离对象顶部地址的偏移量,因为对象的头部就是虚函数表的指针,所以偏移量为0。
RTTI指针:指向存储运行时类型信息(type_info)的地址,用于运行时类型识别,用于typeid和dynamic_cast
。
struct Base {
Base() = default;
virtual ~Base() = default;
void FuncA() {}
virtual void FuncB() {
printf("Base FuncB\n");
}
int a;
int b;
};
struct Derive : public Base{
public:
int c;
};
int main() {
Base a;
Derive d;
return 0;
}
这里的FuncB函数,还是Base类中的FuncB,因为在子类中没有重写这个函数,那么如果子类重写这个函数后对象布局是什么样的,请继续往下看。
4. 单继承下含有覆盖函数的类对象是什么布局?
struct Base {
Base() = default;
virtual ~Base() = default;
void FuncA() {}
virtual void FuncB() {
printf("Base FuncB\n");
}
int a;
int b;
};
struct Derive : public Base{
void FuncB() override {
printf("Derive FuncB \n");
}
int c;
};
int main() {
Base a;
Derive d;
return 0;
}
,表示Base和Derive的虚表地址是相同
struct BaseA {
BaseA() = default;
virtual ~BaseA() = default;
void FuncA() {}
virtual void FuncB() {
printf("BaseA FuncB\n");
}
int a;
int b;
};
struct BaseB {
BaseB() = default;
virtual ~BaseB() = default;
void FuncA() {}
virtual void FuncC() {
printf("BaseB FuncC\n");
}
int c;
int d;
};
struct Derive : public BaseA, public BaseB{
};
int main() {
BaseA a;
Derive d;
return 0;
}
:表示当前这个虚函数表(BaseA,Derive)地址距离对象顶部地址的偏移量,因为对象的头部就是虚函数表的指针,所以偏移量为0。
RTTI中有了两项,表示BaseA和Derive的虚表地址是相同的,BaseA类里的虚函数和Derive类里的虚函数都在这个链条下,截至到offset_to_top(-16)
之前都是BaseA和Derive的虚函数表。
offset_to_top(-16)
:表示当前这个虚函数表(BaseB)地址距离对象顶部地址的偏移量,因为对象的头部就是虚函数表的指针,所以偏移量为-16,这里用于this指针偏移,下一小节会介绍。
struct BaseA {
BaseA() = default;
virtual ~BaseA() = default;
void FuncA() {}
virtual void FuncB() {
printf("BaseA FuncB\n");
}
int a;
int b;
};
struct BaseB {
BaseB() = default;
virtual ~BaseB() = default;
void FuncA() {}
virtual void FuncC() {
printf("BaseB FuncC\n");
}
int c;
int d;
};
struct Derive : public BaseA, public BaseB{
void FuncB() override {
printf("Derive FuncB \n");
}
void FuncC() override {
printf("Derive FuncC \n");
}
};
int main() {
BaseA a;
Derive d;
return 0;
}
7. 多继承中不同的继承顺序产生的类对象布局相同吗?
struct BaseA {
BaseA() = default;
virtual ~BaseA() = default;
void FuncA() {}
virtual void FuncB() {
printf("BaseA FuncB\n");
}
int a;
int b;
};
struct BaseB {
BaseB() = default;
virtual ~BaseB() = default;
void FuncA() {}
virtual void FuncC() {
printf("BaseB FuncC\n");
}
int c;
int d;
};
struct Derive : public BaseB, public BaseA{
void FuncB() override {
printf("Derive FuncB \n");
}
void FuncC() override {
printf("Derive FuncC \n");
}
};
int main() {
BaseA a;
Derive d;
return 0;
}
BaseB的虚函数表指针和数据在上面,BaseA的虚函数表指针和数据在下面,以A,B的顺序继承,对象的布局就是A在上B在下,以B,A的顺序继承,对象的布局就是B在上A在下。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端