Время выполнения полиморфизм - PullRequest
2 голосов
/ 21 марта 2011

Предположим, у меня есть класс A

public class A
{
    public void method()
    {
        //do stuff
    }
}

Также еще один класс B

public class B extends A
{
    public void method()
    {
        //do other stuff
    }
}

Теперь у меня есть следующие утверждения:

A a = new B();
a.method();

Является ли это примером полиморфизма во время выполнения? Если да, то не выполняется ли привязка для ссылки a во время компиляции?

Ответы [ 6 ]

18 голосов
/ 21 марта 2011

Компилятор скажет вам, что это не может работать, потому что нет никаких отношений между A и B, которые позволят вам написать A a = new B();

B либо должен расширять A, либо оба должны реализовывать общий интерфейс с void method().

Вы могли бы очень быстро ответить на это, попробовав это с компилятором. Будьте экспериментатором - это быстрее чем форумы.

ОБНОВЛЕНИЕ:

Это работает, теперь, когда B расширяет A. Связывание, о котором вы заботитесь, динамическое связывание выполняется во время выполнения. Статический тип времени компиляции для переменной «a» - это класс A; во время выполнения он динамически привязан к ссылке типа B. Да, я бы посчитал это примером полиморфизма.

2 голосов
/ 21 марта 2011

Я ждал, чтобы поместить свой ответ в другой пост, в более правильно классифицированную проблему.Ваш вопрос соответствует концепции, я попытался объяснить подробно.но, так как я не знаю, как ссылка отвечает, я просто скопирую / вставлю полный сценарий здесь ... Пожалуйста, пройдите по нему, вы поймете все, что можно понять о вашей проблеме, вы спросили


Допустим, компилятор должен разрешить вызов следующим образом: *

A a = new AA ();// Предположим, что AA является некоторым подклассом класса A
a-> someFunc ();// и мы вызываем метод "someFunc ()" для

*.

Теперь компилятор методично выполнит следующие шаги.

1.) Во-первых, компилятор знает объявленный тип переменной a, поэтому он проверит, есть ли у объявленного типа объекта a (lets call this, class A for time being) метод с именем someFunc () и что он должен бытьpublic. Этот метод может быть либо объявлен в class A, либо он может быть производным от одного из базовых классов класса A, но это не имеет значения для компилятора, и он просто проверяет его существование с помощью своегоспецификатор доступа: public.

  • Нет необходимости говорить, что любая ошибка на этом шаге вызовет ошибку компилятора.

2.) Во-вторых, после проверки метода наБудучи частью класса А., компилятор должен разрешить вызов правильного метода, поскольку многие методы могут быть там с одинаковыми именами (благодаря перегрузке функции). Этот процесс разрешения правильного метода называется overloading resolution. Компилятор достигает этогопо мамесвязывание подписей вызываемого метода со всеми перегруженными методами, которые являются частью класса.Таким образом, из всех someFunc() s будет найдена и рассмотрена только правильная функция someFunc () (сопоставляющая сигнатуры вызываемому методу).

3.) Теперь наступает сложная часть, может случиться так, чтоФункция someFunc () может быть переопределена в одном из подклассов класса A (lets call this class AA and needless to say it is some subclass of A), и эта переменная a (объявленная как тип A) может фактически ссылаться на объект класса AA (это допустимо вC ++ для включения полиморфизма).Теперь, если объявлено, что метод someFunc () имеет тип virtual, в базовом классе (т. Е. Классе A) и someFunc () был переопределен подклассом (ами) A (либо в AA, либо в классах между A и AA), правильная версия someFunc () должна быть найдена компилятором.

  • Теперь представьте, что вы компилятор, и у вас есть задача выяснить, есть ли у класса AA этот метод.Очевидно, что класс AA будет иметь этот метод, поскольку он является подклассом A, и открытый доступ A в классе A уже был проверен на шаге 1 компилятором !!!,Но в качестве альтернативы, как упомянуто в предыдущем абзаце, someFunc () может быть переопределен классом AA (или любым другим классом между A и AA), что и нужно отлавливать компилятору.Следовательно, вы (поскольку вы играете в компиляторе) могли бы сделать систематическую проверку, чтобы найти самый нижний (самый низкий в дереве наследования) переопределенный метод someFunc (), начиная с класса A и заканчивая в классе AA.В этом поиске вы будете искать те же сигнатуры методов, которые были проверены при разрешении перегрузки.Этот метод будет методом, который будет вызван.

  • Теперь вы можете задаться вопросом: «Какого чёрта», этот поиск выполняется каждый раз? ... Ну не совсем. Компилятор знает, что каждый раз обнаруживает это, и поэтому поддерживает структуру данных с именем Virtual Table для каждого типа класса. Представьте себе виртуальную таблицу как отображение из сигнатур методов (которые являются общедоступными) в указатели функций. Эта виртуальная таблица создается компилятором во время процесса компиляции и сохраняется в памяти во время выполнения программы. В нашем примере класс A и класс AA будут иметь свои собственные виртуальные таблицы. И когда компилятор должен найти someFunc () в классе AA (поскольку фактический объект, на который указывает переменная a, имеет тип AA), он просто найдет указатель на функцию через виртуальную таблицу класса AA. Это так же просто, как хэширование в таблице и операция с постоянным временем.

Привет

Avid

1 голос
/ 21 марта 2011

Является ли это примером полиморфизма во время выполнения?

По состоянию на редактирование: да, это будет полиморфизм во время выполнения, поскольку какой метод фактически выполняется, зависит от того, что назначено a.

Редактировать:

Если method() будет статическим, то вызов a.method() всегда приведет к вызову версии A.Однако тогда вы просто напишите A.method(), и если вы этого не сделаете, любая приличная IDE предупредит вас об этом (потому что это может вводить в заблуждение вызов статического метода в экземпляре).

EndEdit.

Полиморфизм времени компиляции был бы механизмом перегрузки, т.е. компилятор решает, использовать ли someMethod(Number) или someMethod(Integer) в зависимости от того, что он знает о передаваемом параметре (если вы передаете Doubleили просто Number это будет первый, если вы передадите Integer, это будет второй).

Если да, то привязка для ссылки a at не выполняетсявремя компиляции?

Что именно вы имеете в виду?Обратите внимание, что тип a будет A, поэтому вы можете назначить все, что расширяет A во время выполнения.Компилятор просто будет жаловаться, если назначенное значение не является A (вы можете обмануть компилятор с помощью приведения, но это просто зло, и если это нарушит вашу программу, вы были предупреждены;))

1 голос
/ 21 марта 2011

Пример кода, который вы дали, недопустим, поэтому я думаю, что ответ заключается в том, что это не своего рода полиморфизм.

Вы не можете назначить класс переменной такого типа. Я предполагаю, что вы, возможно, намереваетесь получить B из A?

0 голосов
/ 21 марта 2011

Если бы он компилировался, это был бы B::method(), который был бы вызван.(только если вы изменили свой код, чтобы класс B наследовал класс A).

В этом случае вы используете свой объект, как если бы он был только A, но если некоторые методы переопределены в B, тогда эти методыВызываются вместо методов А.

0 голосов
/ 21 марта 2011

должно быть

открытый класс B расширяет A {
метод public void ()
{
// делаем другие вещи
}}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...