Различное поведение оператора удаления с виртуальным и не виртуальным деструктором - PullRequest
0 голосов
/ 17 апреля 2019

Когда я использую виртуальный деструктор структуры 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;
}

Но если деструктор структуры M не является виртуальным оператором, новый возвращает тот же адрес.

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;
}

А размеры объектов разные.

Почему это происходит?

1 Ответ

1 голос
/ 17 апреля 2019

Начну со второго вопроса.

"Почему размер объекта отличается?" - ключ virtual здесь.

Каждый class / struct, в котором есть виртуальная функция, содержит указатель на виртуальную таблицу. В этом случае размер M будет равен размеру указателя на вашей машине. Я предполагаю, что у вас есть 64-битная машина, а размер указателя равен 8 байтов. В примере, где ключевое слово «virtual» было удалено, размер пустого класса составляет 1 байт.

Подробнее о виртуальных функциях и таблицах вы можете прочитать здесь: https://pabloariasal.github.io/2017/06/10/understanding-virtual-tables/

О вашем первом вопросе о повторном использовании адресной памяти в куче Я настоятельно рекомендую вам прочитать первую часть https://azeria -labs.com / кучного эксплуатации частей-1-понимание-заместитель Glibc-кучного реализация /

Вкратце, выделение памяти выполняется чанком. В первом примере (с виртуальным деструктором) оба класса расширяются указателем на виртуальную таблицу. Новая выделенная память не помещается в освобожденную часть памяти, поэтому новый адрес найден. Во втором случае новая выделенная память помещается в освобожденное пространство и используется повторно.

Вы можете попытаться перекомпилировать ваш пример с виртуальной функцией, но с удаленным long d из struct C. Может оказаться, что адрес теперь будет прежним.

...