полиморфизм C++ ((X*) y)-> нечто () по сравнению с ((X) *y) .foo ()

При выполнении его во время проектирования (который Вы указываете, что Вы не), это должно помочь Вам:

http://blogs.msdn.com/abhinaba/archive/2005/11/30/498278.aspx

при выполнении его динамично посредством отражения (похож на случай здесь) это могло бы помочь Вам:

http://infosysblogs.com/microsoft/2007/04/loading_multiple_versions_of_s.html

5
задан Matthew Scharley 5 July 2009 в 00:34
поделиться

5 ответов

Вы нарезаете часть объекта 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 .
  • Вызов функции.
10
ответ дан 18 December 2019 в 07:56
поделиться

Приведение всегда (*) создает новый объект того типа, к которому вы приводите, который создается с использованием объекта, который вы приводите.

Приведение к X * создает новый объект указатель (то есть объект типа X *). Он имеет то же значение, что и y , поэтому он по-прежнему указывает на тот же объект типа Y.

Приведение к X создает новый X. Он создается с использованием * y , но в остальном он не имеет ничего общего со старым объектом. В вашем примере foo () вызывается для этого нового «временного» объекта, а не для объекта, на который указывает y .

Вы правы, что динамический полиморфизм применяется только к указатели и ссылки, а не на объекты, и вот почему: если у вас есть указатель на X, то то, на что он указывает, может быть подклассом X. Но если у вас есть X, то это ' X, и ничего больше. виртуальные вызовы были бы бессмысленными.

(*), если оптимизация не допускает пропуск кода, который не меняет результат. Но оптимизации не разрешается изменять то, что вызывается функция foo () .

8
ответ дан 18 December 2019 в 07:56
поделиться

Я считаю, что это просто из-за того, как указан язык. Ссылки и указатели используют позднее связывание везде, где это возможно, а объекты используют раннее связывание. Было бы возможно выполнить позднее связывание в каждом случае (я полагаю), но компилятор, который делал это, не следовал бы спецификациям C ++.

0
ответ дан 18 December 2019 в 07:56
поделиться

Я думаю, что объяснение Дарта Эру правильное, и вот почему я думаю, что C ++ ведет себя таким образом:

Код (X) * y похож на создание локальной переменной типа X Компилятору необходимо выделить пространство sizeof (X) в стеке, и он отбрасывает любые дополнительные данные, включенные в объект типа Y, поэтому, когда вы вызываете foo (), он должен выполнить X-версию. Компилятору было бы сложно вести себя так, чтобы вы могли вызвать версию Y.

Код (X *) y подобен созданию указателя на объект, и компилятор знает, что указанный объект является X или подкласс X. Во время выполнения, когда вы разыменовываете указатель и вызываете foo с помощью «-> foo ()», класс объекта определяется и используется соответствующая функция.

0
ответ дан 18 December 2019 в 07:56
поделиться

Разыменование (часть * 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 дополнительно! -)

2
ответ дан 18 December 2019 в 07:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: