Что такое виртуальная таблица (Virtual Table / vtable) в контексте ООП?

Ответ

Виртуальная таблица (vtable) — это низкоуровневый механизм реализации динамической диспетчеризации (полиморфизма) в компилируемых языках, таких как Swift для классов. Это таблица указателей на функции, которая позволяет вызывать правильную реализацию метода во время выполнения.

Как это работает:

  1. Для каждого класса компилятор создаёт свою vtable, содержащую адреса всех его не-final методов.
  2. Каждый экземпляр класса содержит скрытый указатель (isa в Objective-C) на структуру класса, которая, в свою очередь, содержит vtable.
  3. При вызове метода (например, object.method()) runtime следует по этому указателю, находит в vtable адрес нужной функции и вызывает её.

Пример на Swift:

class Animal {
    func makeSound() { print("Some sound") } // Попадёт в vtable
}

class Dog: Animal {
    override func makeSound() { print("Woof!") } // Переопределяет запись в vtable
}

class Cat: Animal {
    override func makeSound() { print("Meow!") }
    final func purr() { print("Purr") } // НЕ попадёт в vtable, вызов статический
}

let animals: [Animal] = [Dog(), Cat()]
for animal in animals {
    animal.makeSound() // Динамический вызов через vtable: "Woof!", затем "Meow!"
}

Отличия и оптимизации в Swift:

  • Структуры и перечисления (value types) не используют vtable. Вызовы их методов разрешаются статически во время компиляции, что быстрее.
  • Ключевое слово final у класса или метода исключает его из механизма vtable, позволяя компилятору использовать статическую диспетчеризацию и проводить агрессивные оптимизации.
  • dynamic диспетчеризация (например, для совместимости с Objective-C или Key-Value Observing) использует другой, более медленный механизм (objc_msgSend).

Ответ 18+ 🔞

А, слушай, вот эта самая vtable, виртуальная таблица, блядь... Это ж такая штука, которая в мозгах у компилятора, когда он с полиморфизмом классов разбирается. Представь себе, сука, толстый справочник с телефонами, только вместо телефонов — адреса функций, а вместо абонентов — названия методов.

Как эта хуйня крутится:

  1. Компилятор, этот хитрожопый уёбок, для каждого класса лепит свою личную табличку. Туда он записывает, где в памяти лежит код для каждого не-final метода. Это типа меню: «звук животного — вот по этому адресу, жрать — вот по этому».
  2. Каждый объект, который ты создаёшь, тайком носит в кармане бумажку с адресом (это isa указатель), где живёт описание его класса. А в этом описании как раз и лежит ссылка на эту самую vtable.
  3. И вот когда ты пишешь зверь.подайГолос(), программа, эта маньячка, смотрит: «Ага, у меня тут объект, у него адресок класса... Иду туда, нахожу таблицу, ищу в ней строку "подайГолос"... Опа, вот же адрес! Прыгаю туда и выполняю!». Всё это, блядь, в рантайме происходит, отсюда и магия полиморфизма.

Смотри, как это в Swift выглядит, ёпта:

class Animal {
    func makeSound() { print("Some sound") } // Эта функция попадёт в виртуальную таблицу, как миленькая.
}

class Dog: Animal {
    override func makeSound() { print("Woof!") } // А эта перезатрёт запись в таблице для Dog. Теперь там адрес на "Woof!".
}

class Cat: Animal {
    override func makeSound() { print("Meow!") }
    final func purr() { print("Purr") } // А эта — НЕ попадёт в vtable! Она `final`, её вызов вычисляется сразу при компиляции, быстро, без танцев с бубном.
}

let animals: [Animal] = [Dog(), Cat()]
for animal in animals {
    animal.makeSound() // А вот тут-то и работает vtable! Собака — «Woof!», кошка — «Meow!». Динамически, нахуй!
}

А теперь, блядь, самое интересное — где Swift умничает:

  • Структуры и энамы — им похуй на vtable. Они value types, у них всё решается на этапе компиляции. Быстро, как удар током.
  • Ключевое слово final — это типа крик компилятору: «Эй, мудила, не городи тут виртуальные таблицы, этот метод или класс больше никто не переопределит!». И компилятор, довольный такой, делает статический вызов, который овердохуища быстрее.
  • А вот dynamic диспетчеризация — это уже для тяжёлых случаев, совместимость с Objective-C или KVO. Там уже не vtable, а медленный, старый добрый objc_msgSend, который ищет метод по полной программе. Пиздец как небыстро, но что поделать.