При выполнении его во время проектирования (который Вы указываете, что Вы не), это должно помочь Вам:
http://blogs.msdn.com/abhinaba/archive/2005/11/30/498278.aspx
при выполнении его динамично посредством отражения (похож на случай здесь) это могло бы помочь Вам:
http://infosysblogs.com/microsoft/2007/04/loading_multiple_versions_of_s.html
Вы нарезаете часть объекта Y
и копируете объект в объект X
. Затем вызываемая функция вызывается для объекта X
, и, таким образом, вызывается функция X
.
Когда вы указываете тип в C ++ в объявлении или приведении, это означает, что объявленный или приведенный объект на самом деле относится к этому типу, а не к производному типу.
Если вы хотите просто обработать объект, имеющий тип X
(то есть, если вы хотите, чтобы статический тип выражения был X
, но все же хотите, чтобы он для обозначения объекта Y
), затем вы приводите к ссылочному типу
((X&)*y).foo()
. Это вызовет функцию в объекте Y
и не будет ни срезать, ни копировать в X
объект. По шагам, это делает
y
, который имеет тип Y *
. Разыменование дает выражение lvalue типа Y
. Выражение lvalue может фактически обозначать объект производного типа, даже если его статический тип является одним из его базовых. X и
, что является ссылкой на X
. Это даст выражение lvalue типа X
. Ваше исходное приведение сделало
y
. X
. Это приведет к операции копирования в новый объект X
. Результирующим выражением этого является выражение rvalue статического типа X
. Обозначенный динамический тип объекта - также X
, как и все выражения rvalue . Приведение всегда (*) создает новый объект того типа, к которому вы приводите, который создается с использованием объекта, который вы приводите.
Приведение к X * создает новый объект указатель (то есть объект типа X *). Он имеет то же значение, что и y
, поэтому он по-прежнему указывает на тот же объект типа Y.
Приведение к X создает новый X. Он создается с использованием * y
, но в остальном он не имеет ничего общего со старым объектом. В вашем примере foo ()
вызывается для этого нового «временного» объекта, а не для объекта, на который указывает y
.
Вы правы, что динамический полиморфизм применяется только к указатели и ссылки, а не на объекты, и вот почему: если у вас есть указатель на X, то то, на что он указывает, может быть подклассом X. Но если у вас есть X, то это ' X, и ничего больше. виртуальные вызовы были бы бессмысленными.
(*), если оптимизация не допускает пропуск кода, который не меняет результат. Но оптимизации не разрешается изменять то, что вызывается функция foo ()
.
Я считаю, что это просто из-за того, как указан язык. Ссылки и указатели используют позднее связывание везде, где это возможно, а объекты используют раннее связывание. Было бы возможно выполнить позднее связывание в каждом случае (я полагаю), но компилятор, который делал это, не следовал бы спецификациям C ++.
Я думаю, что объяснение Дарта Эру правильное, и вот почему я думаю, что C ++ ведет себя таким образом:
Код (X) * y похож на создание локальной переменной типа X Компилятору необходимо выделить пространство sizeof (X) в стеке, и он отбрасывает любые дополнительные данные, включенные в объект типа Y, поэтому, когда вы вызываете foo (), он должен выполнить X-версию. Компилятору было бы сложно вести себя так, чтобы вы могли вызвать версию Y.
Код (X *) y подобен созданию указателя на объект, и компилятор знает, что указанный объект является X или подкласс X. Во время выполнения, когда вы разыменовываете указатель и вызываете foo с помощью «-> foo ()», класс объекта определяется и используется соответствующая функция.
Разыменование (часть * y
) работает нормально, но приведение (часть (X)
) создает новый (временный) объект конкретно класса X
- вот что означает приведение . Таким образом, объект должен иметь виртуальную таблицу из класса X
- учтите, что приведение типов удалит все члены экземпляра, добавленные Y
в подклассе (действительно, как могло ] X
, возможно, знает о них?), Так что было бы потенциально катастрофой, если бы какое-либо из переопределений Y
было выполнено - им известно, что это
указывает на экземпляр Y
, вместе с добавленными членами и всем ... когда это знание было ложным!
Версия, в которой вы приводите указатели, конечно, совершенно другая - * X
имеет те же биты, что и Y *
, поэтому он по-прежнему указывает на совершенно допустимый экземпляр Y
(на самом деле, он указывает на y
, конечно).
Печальный факт заключается в том, что в целях безопасности объект копирования класса действительно должен вызываться только с в качестве аргумента экземпляр этого класса, а не какого-либо подкласса; потеря добавленных членов экземпляра и c слишком разрушительна. Но единственный способ убедиться в этом - следовать отличному совету Хаара : «Не создавайте подклассы конкретных классов» ... даже несмотря на то, что он пишет о Java, этот совет по крайней мере так же хорош для C ++ ( у которого есть эта проблема "нарезки" копирующего ctor дополнительно! -)
так что он по-прежнему указывает на совершенно допустимый экземпляр Y
(на самом деле, он указывает на y
, конечно).
Печальный факт заключается в том, что в целях безопасности экземпляр ctor класса действительно следует вызывать только с аргументом экземпляра этого класса, а не какого-либо подкласса; потеря добавленных членов экземпляра и c слишком разрушительна. Но единственный способ убедиться в этом - следовать отличному совету Хаара : «Не создавайте подклассы конкретных классов» ... даже несмотря на то, что он пишет о Java, этот совет по крайней мере так же хорош для C ++ ( у которого есть эта проблема "нарезки" копирующего ctor дополнительно! -)
так что он по-прежнему указывает на совершенно допустимый экземпляр Y
(на самом деле, он указывает на y
, конечно).
Печальный факт заключается в том, что в целях безопасности экземпляр ctor класса действительно следует вызывать только с аргументом экземпляра этого класса, а не какого-либо подкласса; потеря добавленных членов экземпляра и c слишком разрушительна. Но единственный способ убедиться в этом - следовать отличному совету Хаара : «Не создавайте подклассы конкретных классов» ... даже несмотря на то, что он пишет о Java, этот совет по крайней мере так же хорош для C ++ ( у которого есть эта проблема "нарезки" копирующего ctor дополнительно! -)
копирующий ctor класса действительно должен вызываться только в качестве аргумента экземпляра этого класса, а не какого-либо подкласса; потеря добавленных членов экземпляра и c слишком разрушительна. Но единственный способ убедиться в этом - следовать отличному совету Хаара : «Не создавайте подклассы конкретных классов» ... даже несмотря на то, что он пишет о Java, этот совет по крайней мере так же хорош для C ++ ( у которого есть эта проблема "нарезки" копирующего ctor дополнительно! -) копирующий ctor класса действительно должен вызываться только в качестве аргумента экземпляра этого класса, а не какого-либо подкласса; потеря добавленных членов экземпляра и c слишком разрушительна. Но единственный способ убедиться в этом - следовать отличному совету Хаара : «Не создавайте подклассы конкретных классов» ... даже несмотря на то, что он пишет о Java, этот совет по крайней мере так же хорош для C ++ ( у которого есть эта проблема "нарезки" копирующего ctor дополнительно! -)