Я думаю, что фраза " классы с виртуальными функциями реализованы с помощью vtables " вводит вас в заблуждение.
Фраза звучит так, как будто классы с виртуальными функциями реализованы " способом A ", а классы без виртуальных функций реализованы " способом B ".
В действительности, классы с виртуальными функциями, в дополнение к , реализуемым как классы, также имеют vtable. Другой способ убедиться в том, что «vtables» реализует часть класса «виртуальная функция».
Подробнее о том, как они оба работают:
Все классы (с виртуальными или не виртуальными методами) являются структурами. Отличие * между структурой и классом в C ++ состоит в том, что по умолчанию члены являются открытыми в структурах и закрытыми в классах. Из-за этого я буду использовать термин «класс» для обозначения структур и классов. Помните, они почти синонимы!
члены данных
Классы - это (как и структуры) просто блоки непрерывной памяти, где каждый член хранится в последовательности. Обратите внимание, что иногда из-за архитектурных причин ЦП будут пропуски между элементами, поэтому блок может быть больше, чем сумма его частей.
Методы
Методы или «функции-члены» являются иллюзией. В действительности не существует такой вещи, как «функция-член». Функция - это всегда последовательность инструкций машинного кода, хранящихся где-то в памяти. Чтобы сделать вызов, процессор переходит на эту позицию памяти и начинает выполнение. Вы могли бы сказать, что все методы и функции являются «глобальными», и любое указание на обратное является удобной иллюзией, поддерживаемой компилятором.
Очевидно, что метод действует так, как будто он принадлежит конкретному объекту, поэтому очевидно, что происходит больше. Чтобы связать конкретный вызов метода (функции) с конкретным объектом, каждый метод-член имеет скрытый аргумент, который является указателем на рассматриваемый объект. Член скрыт в том смысле, что вы сами не добавляете его в свой код C ++, но в этом нет ничего волшебного - это очень реально. Когда вы говорите это:
void CMyThingy::DoSomething(int arg);
{
// do something
}
Компилятор действительно делает это:
void CMyThingy_DoSomething(CMyThingy* this, int arg)
{
/do something
}
Наконец, когда вы пишете это:
myObj.doSomething(aValue);
компилятор говорит:
CMyThingy_DoSomething(&myObj, aValue);
Нет необходимости в указателях функций где-либо! Компилятор уже знает, какой метод вы вызываете, поэтому он вызывает его напрямую.
Статические методы еще проще. У них нет указателя this , поэтому они реализованы именно так, как вы их пишете.
Это так! Остальное - просто удобное синтаксическое суммирование: компилятор знает, к какому классу принадлежит метод, поэтому он гарантирует, что он не позволит вам вызвать функцию, не указав, какой именно. Он также использует эти знания для перевода myItem
в this->myItem
, когда это однозначно.
(да, все верно: доступ к элементу в методе всегда осуществляется косвенно через указатель, даже если вы его не видите)
( Редактировать : удалено последнее предложение и опубликовано отдельно, поэтому его можно критиковать отдельно)