带有虚拟和非虚拟析构函数的删除运算符的不同行为

时间:2019-04-17 13:29:02

标签: c++ inheritance new-operator delete-operator

当我使用结构M的虚拟析构函数时,删除运算符后的new运算符将返回指向其他地址。

import re
len([word for word in text.split() if any(re.match(pattern, word) for pattern in list)])

但是如果结构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;
}

对象的大小不同。

为什么会这样?

1 个答案:

答案 0 :(得分:1)

我将从第二个问题开始。

“为什么对象的大小不同?” -virtual是这里的键。

每个具有虚拟功能的class / struct都包含一个指向虚拟表的指针。 在这种情况下,M的大小将等于计算机上指针的大小。 我猜您有64位计算机,并且指针的大小等于8个字节。 在删除了“虚拟”关键字的示例中,空类的大小为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。事实证明,该地址现在将是相同的。