Когда MDerived*
необходимо преобразовать в Base1*
, компилятор отрегулирует указатель так, чтобы он указывал на правильный адрес памяти, в котором расположены члены этого базового класса. Это означает, что MDerived*
, который передается в Base1*
, может указывать на другой адрес памяти, чем исходный MDerived*
(в зависимости от расположения памяти производного класса).
Компилятор может сделать это, потому что он знает макет памяти всех классов, а когда происходит листинг, он может добавить код, который регулирует адрес указателя.
Например, это может печатать разные адреса:
int main() {
MDerived *d = new MDerived;
std::cout << "derived: " << d << std::endl;
std::cout << "base1: " << (base1*)d << std::endl;
std::cout << "base2: " << (base2*)d << std::endl;
}
В вашем примере такие корректировки не может быть необходимым, так как классы не содержат каких-либо переменных-членов, которые будут использовать любую память в суб-объекты, представляющие базовые классы. Если у вас есть указатель, указывающий на «ничего» (без переменных-членов), на самом деле не имеет значения, что это ничего не называется Base1
или Base2
или MDerived
.
Не виртуальные методы классов не сохраняются с каждым объектом, они хранятся только один раз. Затем компилятор статически во время компиляции использует эти глобальные адреса при вызове функции-члена в соответствии с типом используемой переменной.
Ваш метод «MDerived :: function1» печатал «Base1», что, по-вашему, не так. Я отредактировал ваш пост. –
Может ли кто-нибудь заложить макет памяти, который должен генерировать компилятор. и как производный объект отбрасывается в базовый класс. Потому что в производной памяти есть два базовых класса, как компилятор знает о том, какое смещение выбрать. – mousey
Стандарт не определяет макет памяти, это осталось до исполнителей. Строго говоря, каждый ответ в этом вопросе говорит о специфике платформы, которую некоторые компиляторы используют для реализации виртуальных функций. –