如何查看内存分布
在项目属性中C/C++命令行选项中添加reportSingleClassLayout类名
理解虚函数指针
int* vptr = (int*)*(int*)(&father);
- father是Father类的实例对象
- 解释一下上面的语句
&father
:&
是取地址运算符,用于获取father
这个类实例对象的地址。通过&father
,我们得到一个指向father
对象在内存中存储位置的指针。这个指针的类型通常是Father*
(假设father
是Father
类的实例,这里的Father*
是一个指向Father
类对象的指针)。
(int*)(&father)
:- 这是一个强制类型转换操作,将
&father
的结果(原本是Father*
类型)强制转换为int*
类型。这意味着我们将一个指向类对象的指针强制转换为一个指向整数的指针。这种转换通常是危险的,因为不同类型的对象在内存中的表示和布局可能不同,并且 C++ 中的对象可能包含成员函数、虚函数表指针(如果有虚函数)、成员变量等,它们的布局和大小可能不是简单的整数所能正确表示或解释的。但在某些特殊情况下,如与 C 代码交互或一些低级别的内存操作中,可能会进行这样的转换,但需要非常小心。
- 这是一个强制类型转换操作,将
*(int*)(&father)
:- 对
(int*)(&father)
进行解引用操作。解引用一个int*
指针将得到存储在该指针所指向位置的整数值。在这种情况下,由于我们之前将father
的地址强制转换为int*
,这里解引用会读取内存中father
地址开始处的一些字节,并将其解释为一个int
类型的值。这可能会导致读取到一些未预期的数据,因为它可能包含了类对象的部分或全部内容,而不仅仅是一个简单的整数。
- 对
int* vptr = (int*)*(int*)(&father);
:- 最后,将解引用得到的
int
值再次强制转换为int*
类型,并存储在vptr
指针变量中。这意味着vptr
现在指向一个地址,这个地址是根据father
对象地址处存储的部分数据解释为int
后的值。这种操作可能会导致vptr
指向一个完全错误的内存位置,因为我们之前的转换和操作可能已经破坏了原本正确的内存布局和类型信息。
- 最后,将解引用得到的
typedef void(*func_t) (void);
这是一个函数指针的类型定义。使用 typedef 关键字定义了一个名为func_t
的类型,它是一个指向函数的指针,该函数不接受任何参数(void)并且返回类型为 void
。
理解虚函数表
手绘内存分布:
- 对象内,首先存储的是“虚函数表指针”,又称为“虚表指针”。然后在存储非静态数据成员。
- 对象的非虚函数,保存在类的代码中!
- 对象的内存,只存储虚函数表和数据成员(类的静态数据成员,保存在数据区中,和对象是分开存储的)
- 添加虚函数后,对象的内存空间不变!仅虚函数表中添加条目多个对象,共享同一个虚函数表!
使用继承的虚函数表
多重继承的虚函数表
VS分析:
内存分布
final
用来修饰类,让该类不能被继承
理解:使得该类终结
用来修饰类的虚函数,使得该虚函数在子类中,不能被重写
理解:使得该功能终结
class XiaoMi {
public:
virtual void miliao() f