Новости

c ++ - віртуальна таблиця і схема зберігання _vptr - Qaru

Першим пунктом, про який слід пам'ятати, є відмова від відповідальності: ніщо з цього не гарантується стандартом. У стандарті йдеться, що код повинен виглядати і як він повинен працювати, але насправді не вказує точно, як це має статися компілятором.

Проте, по суті, все компілятори С ++ працюють в цьому відношенні абсолютно аналогічно.

Отже, почнемо з не віртуальних функцій. Вони бувають двох класів: статичні і нестатичні.

Найпростіше це статичні функції-члени. Статична функція-член майже схожа на глобальну функцію, яка friend класу, за винятком того, що вона також потребує імені класу в якості префікса імені функції.

Нестатичні функції-члени трохи складніше. Вони все ще є нормальними функціями, які викликаються безпосередньо, але вони передаються прихованим покажчиком на екземпляр об'єкта, на якому вони були викликані. Усередині функції ви можете використовувати ключове слово this для посилання на дані цього примірника. Отже, коли ви викликаєте щось на зразок a.func (b) ;, що генерується код дуже схожий на код, який ви отримаєте для func (a, b);

Тепер розглянемо віртуальні функції. Тут, де ми отримуємо в vtables і vtable покажчики. У нас досить непрямого уваги, що, ймовірно, найкраще намалювати деякі діаграми, щоб побачити, як все це викладено. Тут досить простий приклад: один екземпляр одного класу з двома віртуальними функціями:

Таким чином, об'єкт містить свої дані і покажчик на таблицю vtable. Vtable містить покажчик на кожну віртуальну функцію, визначену цією класом. Однак, можливо, не відразу видно, чому нам так потрібно торкнутися. Щоб зрозуміти це, давайте подивимося на наступний (все так небагато) більш складний випадок: два примірника цього класу:

Зверніть увагу, що кожен екземпляр класу має свої власні дані, але обидва вони мають один і той же файл vtable і той же код. Якби у нас було більше примірників, вони все одно розділили б одну таблицю vtable серед всіх примірників такої ж клас.

Тепер розглянемо деривацію / успадкування. Як приклад давайте перейменуємо наш існуючий клас в "Base" і додамо похідний клас. Оскільки я відчуваю себе творчо, я назву його "Derived". Як і вище, базовий клас визначає дві віртуальні функції. Похідний клас переопределяет один (але не інший) з них:

Похідний клас переопределяет один (але не інший) з них:

Звичайно, ми можемо об'єднати ці два, маючи кілька примірників кожного з базового і / або похідного класів:

Звичайно, ми можемо об'єднати ці два, маючи кілька примірників кожного з базового і / або похідного класів:

Тепер давайте заглибимося в це трохи докладніше. Цікаво, що ми можемо передати покажчик / посилання на об'єкт похідного класу на функцію, написану для отримання покажчика / посилання на базовий клас, і він все одно працює, але якщо ви викликаєте віртуальну функцію, ви отримуєте версію для фактичного класу, а не базового класу . Отже, як це працює? Як ми можемо розглядати екземпляр похідного класу, як якщо б це був екземпляр базового класу, і все ще він працює? Для цього кожен похідний об'єкт має "подоб'екти базового класу". Наприклад, розглянемо наступний код:

struct simple_base {int a; }; struct simple_derived: public simple_base {int b; };

У цьому випадку, коли ви створюєте екземпляр simple_derived, ви отримуєте об'єкт, який містить два int s: a і b. a (частина базового класу) знаходиться на початку об'єкта в пам'яті, а b (похідна частина класу) дотримується цього. Таким чином, якщо ви передаєте адреса об'єкта функції, яка чекає на екземпляр базового класу, він використовує частини (-и), які існують в базовому класі, які компілятор розміщує з однаковими зсувами в об'єкті, d бути в об'єкті базового класу, тому функція може маніпулювати ними , навіть не знаючи, що вона має справу з об'єктом похідного класу. Аналогічно, якщо ви викликаєте віртуальну функцію, все, що їй потрібно знати, це місце розташування покажчика vtable. Наскільки це важливо, щось на зразок Base :: func1 в основному означає, що він слід вказівником vtable, а потім використовує покажчик на функцію з деяким заданим зміщенням від нього (наприклад, четвертий покажчик функції).

По крайней мере, зараз я проигнорирую множинне спадкування. Це додає досить складну картину (особливо коли задіяно віртуальне успадкування), і ви не згадали про це взагалі, тому я сумніваюся, що вам дійсно все одно.

Що стосується доступу до будь-якого з них або використання будь-яким способом, крім простого виклику віртуальних функцій, ви можете придумати щось для конкретного компілятора, але не чекайте, що він буде взагалі стерпним. Хоча такі речі, як отладчики, часто повинні дивитися на такі речі, використовуваний код має тенденцію бути досить крихким і специфічним для компілятора.

Отже, як це працює?
Як ми можемо розглядати екземпляр похідного класу, як якщо б це був екземпляр базового класу, і все ще він працює?

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

Или позвоните нам по телефонам: (048) 823-25-64

Организация (обязательно) *

Адрес доставки

Объем

Как с вами связаться:

Имя

Телефон (обязательно) *

Мобильный телефон

Ваш E-Mail

Дополнительная информация: