Информация об изменениях

Сообщение Re: Как удаляется vptr? от 10.04.2023 17:40

Изменено 10.04.2023 17:59 rg45

Re: Как удаляется vptr?
Здравствуйте, avovana, Вы писали:

A>Где есть вообще этот vptr?


С уверенностью можно сказать лишь то, что он определен для каждого объекта (по одному на каждый объект). То есть, имея указатель или сылку на объект базового или любого из производных классов, можно получить указатель на таблицу виртуальных функций объекта. А какой размер и формат имеет этот указатель, и в каком месте объекта он располагается (в начале, в конце или вообще в какой-нибудь внешней мапе) — это уже implementation defined, об этом знает только компилятор. Таблицы виртуальных функций являютсся внешними по отношению к объектам. Объекты, имеющие одинаковый статический тип (класс) имеют одинаковый vptr, указывающий на одну и ту же таблицу виртуальных функций.

A>Если делаем объект базового класса, у которого хотя бы 1 функция virtual, то компилятор добавит ему vptr.

A>Размер такого класса(если нет других членов) станет из 1 байта -> sizeof(vptr).

Пока все правильно.

A>Если делаем объект класса наследника от такого базового класса, то компилятор добавит ему vptr. У него не будет ещё 1ого vptr от базового. А будет свой vptr.

A>Т.е. это член объекта наследника.

А вот тут немного по-другому: все производные классы вместе с базовым используют один и тот же указатель vptr, но в зависимости от реального (статического) типа объекта, в этом поле будут записаны разные указатели на разные виртуальные таблицы. Когда объект конструируется, вызываются последовательно конструкторы разных уровней — от базового к самому последнему производному, и в процессе перехода от одного конструктора к другому меняется значение vptr. И это, в принципе можно даже отследить теми или иными отладочными трюками. При разрушении объекта происходит обратный процесс: последовательно вызываются деструкторы от самого последного произодного до базого и при этом так же меняются значения vptr, но уже в обратном порядке. И вот поэтому нельзя вызвать виртуальную фунцию производного класса из конструктора или деструктора базового класса. Точнее, вызвать то можно, но вызовется функция, соответстующая текущему значению vptr.

A>Что если удаляем теперь наследника как в коде ниже. То есть, не будет вызван деструктор класса наследника. Что станет с этим vptr объекта класса наследника? Утечёт?


А при удалении объекта по указателю на базовый класс, код, сгенерированный компилятором, воспользуется значением vptr, хранящимся в объекте и по таблице виртуальных функций вызовет виртуальный деструктор самого последнего производного класса. А тот, в свою очередь инициирует всю цепочку деструкторов до самого базового класса (уже без использования виртуального механизма). После того, как цепочка деструкторов завершится, будет освобождена память, занимаемая объектом.

Что еще важно помнить, что если деструктор базового класса объявлен виртуальным, то деструкторы всех производных классов также будут виртуальными, независимо от того, использовалось ли ключевое слово virtual при их объявлении.
Re: Как удаляется vptr?
Здравствуйте, avovana, Вы писали:

A>Где есть вообще этот vptr?


С уверенностью можно сказать лишь то, что он определен для каждого объекта (по одному на каждый объект). То есть, имея указатель или сылку на объект базового или любого из производных классов, можно получить указатель на таблицу виртуальных функций объекта. А какой размер и формат имеет этот указатель, и в каком месте объекта он располагается (в начале, в конце или вообще в какой-нибудь внешней мапе) — это уже implementation defined, об этом знает только компилятор. Таблицы виртуальных функций являютсся внешними по отношению к объектам. Объекты, имеющие одинаковый статический тип (класс) имеют одинаковый vptr, указывающий на одну и ту же таблицу виртуальных функций.

A>Если делаем объект базового класса, у которого хотя бы 1 функция virtual, то компилятор добавит ему vptr.

A>Размер такого класса(если нет других членов) станет из 1 байта -> sizeof(vptr).

Пока все правильно.

A>Если делаем объект класса наследника от такого базового класса, то компилятор добавит ему vptr. У него не будет ещё 1ого vptr от базового. А будет свой vptr.

A>Т.е. это член объекта наследника.

А вот тут немного по-другому: все производные классы вместе с базовым используют один и тот же указатель vptr, но в зависимости от реального (статического) типа объекта, в этом поле будут записаны разные указатели на разные виртуальные таблицы. Когда объект конструируется, вызываются последовательно конструкторы разных уровней — от базового к самому последнему производному. Внутри каждого конструктора, vptr указывает на таблицу виртуальных функций, соответствующую тому классу, который конструируется в данный момент. , При перехода от одного конструктора к другому, изменяется и значение vptr. Этот процесс изменения vptr можно отследить теми или иными отладочными трюками, если предварительно вычислить (не очень законными способами) формат и место расположения vptr в объекте. При разрушении объекта происходит обратный процесс: последовательно вызываются деструкторы от самого последного произодного до базого и при этом так же меняются значения vptr, но уже в обратном порядке. И вот поэтому нельзя вызвать виртуальную фунцию производного класса из конструктора или деструктора базового класса. Точнее, вызвать то можно, но вызовется функция, соответстующая текущему значению vptr.

A>Что если удаляем теперь наследника как в коде ниже. То есть, не будет вызван деструктор класса наследника. Что станет с этим vptr объекта класса наследника? Утечёт?


А при удалении объекта по указателю на базовый класс, код, сгенерированный компилятором, воспользуется значением vptr, хранящимся в объекте и по таблице виртуальных функций вызовет виртуальный деструктор самого последнего производного класса. А тот, в свою очередь инициирует всю цепочку деструкторов до самого базового класса (уже без использования виртуального механизма). После того, как цепочка деструкторов завершится, будет освобождена память, занимаемая объектом.

Что еще важно помнить, что если деструктор базового класса объявлен виртуальным, то деструкторы всех производных классов также будут виртуальными, независимо от того, использовалось ли ключевое слово virtual при их объявлении.