c++ - 具有虚拟和非虚拟析构函数的删除运算符的不同行为
问题描述
当我使用 struct M 的虚拟析构函数时,删除运算符返回后的运算符 new 指向其他地址。
struct M {
virtual ~M() = default;
};
struct D : public M {
int* b = nullptr;
};
struct C : public M {
int* c = nullptr, *b = nullptr;
long d = 10;
};
int main() {
M* f;
M* d;
f = new D;
static_cast<D*>(f)->b = new int(10);
std::cout << f << ":" << sizeof(*f) << std::endl; // 0x23c1c20 : 8
delete f;
d = new C;
std::cout << d << ":" << sizeof(*d) << std::endl; // 0x23c2c70 : 8
delete d;
return 0;
}
但是如果 struct M 的析构函数是非虚运算符 new 返回相同的地址。
struct M {
~M() = default;
};
...
int main() {
M* f;
M* d;
f = new D;
static_cast<D*>(f)->b = new int(10);
std::cout << f << ":" << sizeof(*f) << std::endl; // 0x23c1c20 : 1
delete f;
d = new C;
std::cout << d << ":" << sizeof(*d) << std::endl; // 0x23c1c20 : 1
delete d;
return 0;
}
并且物体的大小是不同的。
为什么会这样?
解决方案
我将从第二个问题开始。
“为什么物体的大小不一样?” - 这virtual
是这里的关键。
每个具有虚函数的class
/struct
都包含一个指向虚表的指针。在这种情况下,M 的大小将等于您机器上指针的大小。我猜你有 64 位机器,指针的大小等于 8 个字节。在删除“virtual”关键字的示例中,空类的大小为 1 个字节。
有关虚函数和表的更多信息,您可以在此处阅读: https ://pabloariasal.github.io/2017/06/10/understanding-virtual-tables/
关于在堆上重用地址内存的第一个问题,我强烈建议您阅读 https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/的第一部分
简而言之,内存的分配是按块来完成的。在第一个示例中(使用虚拟析构函数),两个类都通过指向虚拟表的指针进行扩展。新分配的内存不适合重新分配的内存块,因此找到了新地址。在第二个中,新分配的内存适合释放的空间并被重用。
您可以尝试使用虚函数重新编译您的示例,long d
但从struct C
. 事实证明,现在地址将是相同的。
推荐阅读
- python - 调试时使用控制台命令绘制图形
- php - 将产品图片(不是缩略图)添加到 WooCommerce 订单电子邮件
- java - Java8容器`for each`和Stream`for each`有什么区别
- jquery - 强制滚动到外部网站上的特定最高值
- vb.net - 使表单最大化,但不是全屏
- loops - (大 O)查找嵌套循环中的迭代次数
- ios - Firebase Analytics iOS 有效的自动续订本地收据验证?
- php - 在 laravel 中使用 tinymce 插入数据。但显示显示 css 样式。如何删除显示中的css样式
- python - 从 Python 字典中查找并删除冒号分隔的值(IPv6 地址)
- python - 如何确保 pip 将软件包安装到 python 3?