Предположим, что Y - производный класс из класса X, а X объявляет foo виртуальным. Пусть y имеет тип (Y *). Тогда ((X *) y) -> foo() выполнит Y-версию foo(), но ((X) * y) .foo() выполнит версию X. Можете ли вы сказать мне, почему полиморфизм не применяется в случае разыменования? Я ожидаю, что любой синтаксис даст версию Y foo().C++ полиморфизм ((X *) y) -> foo() vs ((X) * y) .foo()
ответ
Я считаю, что это просто из-за того, как указывается язык. Ссылки и указатели используют, по возможности, позднее связывание, в то время как объекты используют раннее связывание. Можно было бы связать последнее связывание во всех случаях (я думаю), но компилятор, который сделал это, не будет следовать спецификациям C++.
Листинг всегда (*) создает новый объект типа, на который вы клонируете, который построен с использованием объекта, который вы выполняете.
Кастинг для X * создает новый указатель (то есть объект типа X *). Он имеет то же значение, что и y
, поэтому он все еще указывает на тот же объект типа Y.
Кастинг для X создает новый X. Он построен с использованием *y
, но в остальном он не имеет ничего общего со старым объектом , В вашем примере foo()
вызывается на этот новый «временный» объект, а не на объект, на который указывает y
.
Вы правы, что динамический полиморфизм применяется только к указателям и ссылкам, а не к объектам, и именно по этой причине: если у вас есть указатель на X, то то, на что он указывает, может быть подклассом X. Но если у вас есть X, то это X, и больше ничего. виртуальные вызовы были бы бессмысленными.
(*) если оптимизация не позволяет исключить код, который не изменит результат. Но оптимизация не позволяет изменить то, что вызывается функцией foo()
.
Я думаю, что объяснение Дарта Эры является правильным, и вот почему я думаю, что C++ ведет себя таким образом:
кода (X) * у, как создать локальную переменную, которая имеет типа X. компилятор должен выделять пространство sizeof (X) в стеке и отбрасывать любые дополнительные данные, включенные в объект типа Y, поэтому при вызове foo() он должен выполнить версию X. Компилятору будет сложно вести себя таким образом, чтобы вы могли назвать версию Y.
Код (X *) y как создание указателя на объект, и компилятор знает, что объект, на который указывает объект, является X или подклассом X. Во время выполнения, когда вы разыскиваете указатель и вызываете foo с помощью "- > foo() "определяется класс объекта и используется правильная функция.
Помните, что (X) * y определяется как X (* y), то есть вызов некоторого конструктора X для создания нового объекта X. Это было бы не просто сложно, даже не желательно, чтобы объект X имел Y-версию foo(), вызываемую на нем, только потому, что он был построен с использованием объекта Y. –
разыменования (*y
части) в порядке, но бросок ((X)
части) создает новый (временный) объект специально класса X
- это то, что отливка означает. Таким образом, объект должен иметь виртуальную таблицу из класса X
- подумайте, что в литье будут удалены все члены экземпляра, добавленные Y
в подклассу (действительно, как может быть, может быть, о них может быть)? потенциально может быть катастрофой, если какой-либо из переопределений Y
должен был выполнить - насколько они знают, что this
указывает на экземпляр Y
, в комплекте с добавленными членами и все ... когда это знание было ложным!
версия, в которой вы разыгрываете указатели, конечно, совершенно разные - *X
имеет только одни и те же биты, как Y*
, так что по-прежнему указывает на вполне допустимый экземпляр Y
(на самом деле, это, указывая на y
, конечно).
Печальный факт заключается в том, что для обеспечения безопасности копия ctor класса действительно должна быть вызвана, как аргумент, экземпляром этого класса - не любого подкласса; потеря добавленных членов экземпляра & c просто повреждает. Но единственный способ убедиться в том, что следует следовать отличному совету Haahr «Не подклассы конкретных классов» ... хотя он пишет о Java, совет, по крайней мере, хорош для C++ (у которого есть эта копия ctor) slicing "!)
Если потеря дополнительных членов Y повреждается при построении копирования X, то либо класс Y не удовлетворяет принципу подписи Лискова, либо, скорее всего, вызывающий не понял, что он кастинг (непреднамеренный фрагмент), и считает, что он все еще имеет объект класса Y. В любом случае, я не думаю, что ошибка, как таковая, вызывает конструктор копирования с Y. Он не понимает, что результатом является X, построенный из Y, как и любой другой конструктор 1-arg , а не что-то полиморфное. –
Непредвиденный фрагмент является наиболее вероятной причиной, и если вы не подклассы конкретных классов (в дополнение к другим причинам Haahr дает), это один вид несчастного случая, которого не произойдет! -) –
Конечно, это правильное правило большой палец. Я также согласен с Хааром, есть случаи, когда стоит подцепить вашу руку и подклассифицировать, потому что иногда наследование действительно полезно. Но на самом деле можно было бы удовлетворить Лискова, в котором большинство подклассов конкретных классов ошибочно. Если, как говорит Хаар, * любой * аспект суперкласса «затаскивается непреднамеренно», то вы уже потерпели неудачу. Но вы можете подклассы конкретных классов, в редких случаях это имеет смысл. Если это так, копия ctor имеет смысл, и те, кто понимает, что нарезка, в любом случае избегают этого, не делая этого. –
Вы разрезаете часть объекта Y
и копируете объект в объект X
. Вызываемая функция вызывается на объект X
, и, следовательно, вызывается функция X
.
Когда вы укажете тип в C++ в объявлении или акте, это означает, что объявленный объект или casted-to фактически относится к этому типу, а не к производному типу.
Если вы хотите, чтобы просто рассматривать объект является быть типа X
(то есть, если вы хотите статический тип выражения быть X
, но все-таки хочу, чтобы обозначить Y
объект), то вы применяете к ссылочный тип
((X&)*y).foo()
Это будет вызывать функцию в Y
объекта, а не резать и не копировать в X
объекта. На этапах, это делает
- разыменования указателя
y
, который имеет типY*
. Выделение вызывает lvalue выражение типаY
. Выражение lvalue может фактически обозначать объект производного типа, даже если его статический тип является его базой. - Относится к
X&
, что является ссылкой наX
. Это даст lvalue выражение типаX
. - Вызовите функцию.
Ваш оригинальный бросок сделал
- разыменования указателя
y
. - Полученное выражение, отлитое от
X
. Это приведет к операции копирования в новый объектX
. В результате это выражение представляет собой выражение rvalue выражение статического типаX
. Динамический тип обозначенного объекта: такжеX
, как и все rvalue выражения. - Вызовите функцию.
- 1. x, y = getPos() vs. (x, y) = getPos()
- 2. AngularJS: `{{x + ',' + y}}` vs. `{{x}}, {{y}}`
- 3. C++: почему foo (X &) вызывается вместо foo (X &&)?
- 4. Использование и альтернативы синтаксиса «my $ foo = $ x if $ y»?
- 5. HibernateException: идентификатор экземпляра foo был изменен с X на Y
- 6. ANTLR грамматика [Foo] - [бар] - [Баз] [X] - [Y] [Z]
- 7. Что не так в синтаксисе «my $ foo = $ x if $ y»?
- 8. Несоответствие производительности: obj .__ setitem __ (x, y) vs. obj [x] = y?
- 9. Широта/Долгота -> X/Y, X/Y -> Широта/Долгота
- 10. EF безопасная комбинация лямбда - (x, y) => x> y в (x) => x> константа
- 11. Заменить x с y или добавить y, если нет x
- 12. представление значений (x, y) vs x._1, y._1
- 13. matlab sum (X-Y) vs sum (X) - sum (Y)
- 14. jquery: .data ('x', 'y') vs .attr ('data-x', 'y')
- 15. differnce между sum (x, y) vs sum.call (undefined, x, y)
- 16. Экспоненты в python x. ** y vs math.pow (x, y)
- 17. Разница между if (x) {foo(); } и x? foo(): 0;
- 18. Regexp x.get (y) -> x [y]
- 19. Scala - Операция в случае (x, y) => x ++ y
- 20. Is (x <= y) или! (x> y) более эффективно?
- 21. Как реализуется реализация функции Eq typeclass: x == y = not (x/= y) x/= y = not (x == y)?
- 22. EASIEST Способ преобразования изображения SVG с использованием ARBITRARY Функция (x, y) -> (x '(x, y), y' (x, y))
- 23. $ ("'" + foo + "'") vs $ (foo)
- 24. Как сделать цикл бесконечным с "x <= y && x > = y && x! = Y"?
- 25. [] .concat (["x", "y"], "z") -> Как получить [["x", "y"], "z"] вместо ["x", "y" , "г"]?
- 26. Разница между (X! = Y) и (! (X == Y))?
- 27. разница между ∀x∃y и ∃y∀x
- 28. C: как проверить, когда координируется [x] [y] == coord [x] [y]?
- 29. #define FUNC (x, y) x =^y; у^х; in c
- 30. конденсаторные X, Y на Y
... и ((X &) * y) .foo() _does_ вызывает производное foo, потому что приведение Y & к X и не создает новый X. – Doug
Да, я должен, вероятно, сказать: «всегда создает новый объект типа, на который вы производите, если только тип, который вы выполняете, не является типом объекта». Кастинг для ссылочного типа «создает» новую ссылку, связанную с исходным объектом. –