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

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

Изменено 10.04.2023 17:40 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.

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


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

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