C ++ Interview: vtable для класса с чистой виртуальной функцией

Мне задали этот вопрос на интервью сегодня !! (это было действительно неудобное телефонное интервью ..):

В чем разница между vtable для класса с виртуальными функциями и класса с чистыми виртуальными функциями?

Теперь я знаю, что в стандарте C ++ ничего не говорится о vtables или даже о существовании v-таблицы ... но теоретически, каков будет ответ?

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

Сможет ли гипотетический компилятор создать виртуальную таблицу для класса только с чистыми виртуальными функциями? Что, если класс содержит чистые виртуальные функции с определениями? (как показано на: http://www.gotw.ca/gotw/031.htm).


person user7    schedule 03.10.2011    source источник
comment
телефонное интервью? у тебя не было доступа к гуглу?   -  person Mitch Wheat    schedule 03.10.2011
comment
Если бы я был менеджером и слышал, как печатал во время телефонного интервью, думаю, я бы тут же подвел человека: p   -  person John Humphreys    schedule 03.10.2011
comment
Я не сказал, что это хорошая идея, или что я согласен с этим. Посмотрим правде в глаза: если кандидат недостаточно умен, чтобы скрыть факт использования Google, вы бы наняли его?   -  person Mitch Wheat    schedule 03.10.2011
comment
Скрывать свои действия намного хуже, чем использовать Google. Интересно, сколько успешных инженеров прожили неделю без использования Google? Как насчет дня? Отвечая на этот вопрос, вы тоже не станете хорошим инженером, как раз наоборот.   -  person Tom Kerr    schedule 03.10.2011
comment
Раньше я пользовался Google на телефонных собеседованиях, и обычно я чувствую себя грязным. Я бы предпочел просто сказать, я не знаю, мне придется поискать это.   -  person gred    schedule 03.10.2011


Ответы (3)


В случае виртуальных функций, не являющихся чистыми, каждая запись в vtable будет ссылаться на final-overrider или thunk, который при необходимости адаптирует указатель this. В случае чистой виртуальной функции запись в vtable обычно содержит указатель на универсальную функцию, которая жалуется и прерывает программу с некоторым разумным сообщением (чистая виртуальная функция, вызываемая внутри этого context или подобное сообщение об ошибке).

Сможет ли гипотетический компилятор создать виртуальную таблицу для класса только с чистыми виртуальными функциями?

Да, будет, разница будет в содержимом, хранящемся в таблице, а не в форме таблицы. В упрощенном подходе указатель NULL для чистых виртуальных функций, не NULL для виртуальных функций. Реально указатель на универсальную функцию, которая будет жаловаться abort() с обычными компиляторами.

Что, если класс содержит чистые виртуальные функции с определениями?

Это не повлияет на vtable. Таблица vtable используется только для динамической отправки, и вызов никогда не будет динамически отправляться для определения чистой виртуальной функции (т. Е. Вы можете выполнить отправку чистой виртуальной функции только вручную, отключив динамическую отправку с указанием имени типа: x.base::f() will вызовите base::f, даже если он чисто виртуальный, но x.f() никогда не будет отправлен на base::f, если он чисто виртуальный.

person David Rodríguez - dribeas    schedule 03.10.2011
comment
Преобразователь нужен только для множественного наследования, не так ли? При одиночном наследовании базовая и производная версии начинаются в одном месте, поэтому нет необходимости адаптировать this. - person eran; 03.10.2011
comment
@eran: верно, в иерархии линейного наследования все объекты с виртуальными функциями, скорее всего, будут иметь один и тот же адрес. - person Matthieu M.; 03.10.2011
comment
@MatthieuM: в иерархии линейного наследования все объекты с виртуальными функциями, скорее всего, будут иметь один и тот же адрес: Почему? - person user7; 03.10.2011
comment
@MatthieuM .: просто нулевой указатель соответствует стандарту, но все известные мне реализации добавляют вспомогательную функцию. Это каким-то образом низко висящий плод для улучшения качества реализации, зачем просто вызывать сбой, если вы можете предоставить информацию о том, что пошло не так и сбой? - person David Rodríguez - dribeas; 03.10.2011
comment
@ user7: преобразователь требуется только в тех случаях, когда указатели на базовый и производный классы отличаются. В отношении единственного наследования подобъект base выравнивается с подобъектом derived, и это означает, что (void*)static_cast<base*>(p) == (void*)p, где p - указатель на производный (расположение в памяти base и derived совпадает). Когда у вас множественное наследование, только одна из баз может быть выровнена с объектом derived, поэтому для одной из множественных баз указатели совпадают, но для остальных вам нужно исправить указатель this. - person David Rodríguez - dribeas; 03.10.2011
comment
@ DavidRodríguez-dribeas: Кажется, я помню сбои с gcc 3.4 (да, он старый) в режиме выпуска, может быть, неправильно, это было несколько раз назад. - person Matthieu M.; 03.10.2011

В таких случаях реализация может делать практически все, потому что, если ваш код вызывает чистую виртуальную функцию в контексте, где требуется динамическое разрешение, и он разрешается в чистую виртуальную функцию, поведение не определено. Я видел несколько разных решений: компилятор вставляет адрес функции, которая завершается сообщением об ошибке (предпочтительное решение с точки зрения качества реализации), компилятор вставляет нулевой указатель или компилятор вставляет адрес функция из некоторого производного класса. Я также видел случаи, когда компилятор вставлял адрес функции, если вы предоставляли реализацию. Единственно правильный ответ на вопрос - нельзя рассчитывать на какое-то конкретное поведение.

person James Kanze    schedule 03.10.2011

Я могу сказать вам, что «чистые» абстрактные классы (классы только с чисто виртуальными функциями) используются Microsoft (и MS VC ++) для своих COM-интерфейсов. Возможно, он говорил об этом. «Внутреннее» представление COM - это указатель на vtable. Чистые абстрактные классы в MS VC ++ реализованы таким же образом, поэтому вы можете использовать их для представления COM-объектов. Очевидно, что если у вашего класса есть другие виртуальные функции, вы не можете просто перезаписать его vtable с помощью COM vtable :-)

person xanatos    schedule 03.10.2011
comment
IIRC, абстрактный класс - это класс с одним или несколькими чистыми виртуальными методами, тогда как класс интерфейса - это абстрактный класс только с чистыми виртуальными методами и без переменных-членов. Я никогда не встречал терминов «абстрактный» и «интерфейс», кодифицированных в C ++. Я экстраполирую из C # и Java. - person paercebal; 03.10.2011
comment
@paercebal Но в C ++ нет interface :-) Правильный термин - pure abstract class (и я забыл его написать :-)) - person xanatos; 03.10.2011
comment
But there aren't "interfaces" in C++: В частности, ключевое слово interface не существует в C ++. Эта концепция существует, поддерживается языком и даже расширяется по сравнению с interface в Java. - person paercebal; 03.10.2011
comment
@paercebal Обратите внимание, как я отформатировал interfaces в своем комментарии :-) Уловка была там. У вас могут быть интерфейсы даже без чистых абстрактных классов. Интерфейсы существуют прежде всего в сознании программиста. Класс, в котором каждый виртуальный метод генерирует и должен быть перегружен, чтобы что-то делать, для меня является интерфейсом. Вам не нужна языковая поддержка, отличная от полиморфизма. Вы можете использовать COM с C, а C даже не имеет классов, а COM основан на интерфейсах. Как я уже сказал, интерфейсы находятся в сознании программиста. Язык может помечать их только как ошибку. - person xanatos; 04.10.2011