При приведении к другому типу смещения полей, а также записей в vtable должны находиться в согласованном месте. Код, который принимает ICommonBase*
в качестве параметра, не знает, что ваш объект действительно IDerived2
. Тем не менее, он все еще должен иметь возможность разыменования ->foo
или вызова виртуального метода bar()
. Если они не по предсказуемым адресам, у которых нет способа работать.
Для случая единственного наследования это легко сделать правильно. Если Derived
наследуется от Base
, вы можете просто сказать, что смещение 0 в Derived
также равно 0 в Base
, и элементы, уникальные для Derived
, могут идти после последнего члена Base
. Очевидно, что для множественного наследования это не может работать, поскольку первый байт Base1
также не может быть первым байтом Base2
. Каждому нужно свое место.
Таким образом, если у вас есть такой класс, который наследует от двух (назовите его Foo
), компилятор может знать, что для типа Foo
часть Base1
начинается со смещения X, а часть Base2
начинается со смещения Y. При приведении к любому из типов компилятор может просто добавить соответствующее смещение к this
.
Когда вызывается фактический виртуальный метод Foo
, где реализация обеспечивается Foo
, ему все еще нужен «реальный» указатель на объект, чтобы он мог получить доступ ко всем его членам, а не только к конкретный экземпляр базы Base1
или Base2
. Следовательно, this
все еще нужно указывать на "настоящий" объект.
Обратите внимание, что детали реализации этого могут отличаться от описанных, это просто высокоуровневое описание того, почему существует проблема.