Почему удаляющий деструктор занимает второй слот vtable помимо обычного деструктора? - PullRequest
1 голос
/ 25 февраля 2020

В реализациях C ++ ABI, смоделированных после Itanium C ++ ABI , за которым следуют многие ABI для других процессоров, виртуальные деструкторы фактически занимают два слота vtable. Помимо «полного деструктора объекта», который делает то, что вы ожидаете, есть вторая запись для «удаления деструктора», который вызывает первый, а затем удаляет память объекта.

Существует Проблема с этим подходом, который может создавать неудобства в небольших системах памяти: менеджер памяти Dynami c связан даже тогда, когда никакой другой код не использует его. Это мертвый код, когда в приложении нет вызова для удаления. Это связано с тем, что компилятор / компоновщик C ++ обычно не может обнаружить, что слот в vtable не вызывается откуда-либо, и, следовательно, удалить связанный код. Ясно, что было бы лучше, если бы деструктор удаления мог бы быть реализован другим способом, который не включает запись vtable и позволяет компилятору / компоновщику пропустить этот мертвый код.

Конечно, можно реализовать custom void operator delete(void *) {}, чтобы запретить компоновщику вводить динамический код памяти c, но это по-прежнему не мешает удалению кода деструктора полностью.

Отсюда мой вопрос: нет ли лучшего способа реализовать удаление деструкторов? Моя идея состояла бы в том, чтобы вернуть указатель на начало блока памяти, чтобы удалить его из полного деструктора объекта. Если блок памяти должен быть удален после уничтожения, этот возвращенный адрес может использоваться не виртуальной функцией, которая вызывает operator delete. По сути, наличие адреса памяти, возвращаемого деструктором полного объекта, позволило бы удалить деструктор не виртуальным и, следовательно, подходящим для удаления мертвого кода.

Но я полагаю, что я что-то упустил, что делает это довольно простое решение невозможно. Но что это будет? Может кто-нибудь изложить для меня решение о дизайне в Itanium ABI?

Редактировать: Я нашел информацию, которая дает частичный ответ здесь :

В верхнем ответе содержится это объяснение:

Когда некоторый класс определяет свой собственный оператор удаления, выбор определенного оператора удаления для вызова c выполняется так, как если бы он был найден внутри класса. деструктор. Конечным результатом этого является то, что для классов с оператором виртуального деструктора удаление ведет себя так, как если бы это была виртуальная функция (несмотря на то, что формально она является членом класса c).

Очевидно, способ Itanium API, выбранный для того, чтобы заставить вести себя как виртуальную функцию, означает сделать деструктор, который вызывает ее, фактическую виртуальную функцию.

Однако это не так единственный способ реализовать это. Связанная статья посвящена реализации, которая использует одну виртуальную функцию со скрытым параметром, но это решение вызывает то же нежелательное поведение, которое я описывал выше. Другая реализация может состоять в том, чтобы полный деструктор объекта возвращал адрес оператора delete (), если есть специальная реализация для класса, и nullptr в противном случае. Это позволило бы избежать проблемы, которую я описал выше.

Итак, в несколько измененном виде мой вопрос все еще стоит.

1 Ответ

0 голосов
/ 25 февраля 2020

В отличие от обычных виртуальных функций при уничтожении класса, его деструктор и деструкторы всех его родителей должны вызываться в правильном порядке.

Технически возможно объединить все вызовы функций в одну функцию. вызов, но это потребовало бы знания реализации каждого деструктора или полной структуры объекта. В основном де-виртуализация вызова. Компиляторы не очень хороши в этом. Не уверен во всех деталях, так как это довольно сложный вопрос, учитывая все возможные чрезмерно виртуальные объекты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...