Объект foo
является местной переменной типа Foo*
. Вероятно, эта переменная распределяется по стеку для функции main
, как и любая другая локальная переменная. Но значение , хранящееся в foo
, является нулевым указателем. Он нигде не указывает. Нет экземпляра типа Foo
, представленного в любом месте.
Для вызова виртуальной функции вызывающему абоненту необходимо знать, к какому объекту вызывается функция. Это потому, что сам объект - это то, что говорит, какую функцию нужно действительно вызывать. (Это часто реализуется путем предоставления объекту указателя на таблицу vtable, списка указателей функций, и вызывающий абонент просто знает, что он должен вызвать первую функцию в списке, не зная заранее, где указывает этот указатель.)
Но для вызова не виртуальной функции вызывающему абоненту не нужно все это знать. Компилятор точно знает, какая функция будет вызвана, поэтому он может сгенерировать инструкцию машинного кода CALL
, чтобы перейти непосредственно к нужной функции. Он просто передает указатель на объект, который функция вызывала как скрытый параметр для функции. Другими словами, компилятор переводит ваш вызов функции в этом:
void Foo_say_hi(Foo* this);
Foo_say_hi(foo);
Теперь, поскольку реализация этой функции никогда не делает ссылки на любые члены указываемого объекта его this
аргумент, вы эффективно увернуться от пули из разыменовывая нулевой указатель, потому что вы никогда не разыскиваете его.
Формально, по телефону любая функция - даже не виртуальная - по нулевому указателю - неопределенное поведение. Одним из допустимых результатов неопределенного поведения является то, что ваш код работает точно так, как вы предполагали. Вам не следует полагаться на это, хотя иногда вы найдете библиотеки от своего поставщика компилятора, которые полагаются на это do. Но у поставщика компилятора есть преимущество, заключающееся в возможности добавить дополнительное определение к тому, что иначе было бы неопределенным поведением. Не делай этого сам.
См. [Это] (http://stackoverflow.com/questions/2474018/when-does-invoking-a-member-function-on-a-null-instance-result-in-undefined-behav) для чего язык говорит об этом. Оба являются неопределенным поведением. – GManNickG