2010-10-06 2 views
5

Сегодня Недавно на Stackoverflow я узнал, что:Delphi: Когда повторно вводит скрытых предков и когда они показывают их?

я пытался разобраться во всем этом, так вот еще один, очень конкретный вопрос , supporting my main question dealing with constructors.


Update: заменить весь вопрос:

TComputer = class(TObject) 
public 
    constructor Create(Teapot: string=''); 
end; 

TCellPhone = class(TComputer) 
public 
    constructor Create(Cup: Integer); overload; virtual; 
    constructor Create(Cup: Integer; Teapot: string); overload; virtual; 
end; 

При построении TCellPhone, 3 Конструкторы В комплекте:

  • Cup: Integer
  • Cup: Integer; Чайник: строка
  • [чайник: String = '']

Вопрос: Почему constructor(Teapot: string='') не скрыт?


Теперь я добавил 3-й потомок:

TComputer = class(TObject) 
public 
    constructor Create(Teapot: string=''); 
end; 

TCellPhone = class(TComputer) 
public 
    constructor Create(Cup: Integer); overload; virtual; 
    constructor Create(Cup: Integer; Teapot: string); overload; virtual; 
end; 

TiPhone = class(TCellPhone) 
public 
    constructor Create(Cup: Integer); override; 
end; 

При построении TiPhoneчетыре Конструкторы доступны:

  • Cup: Integer
  • Cup: Integer
  • Кубок: целое; Чайник: строка
  • [чайник: строка = '']

Почему там четыре конструкторы? я перевернул одну из существующих трех. Редактировать: Это может быть ошибка в понимании кода, это показывает мне четыре - но как я могу позвонить тогда, когда два одинаковые.


Используя исходный код еще раз:

TComputer = class(TObject) 
public 
    constructor Create(Teapot: string=''); 
end; 

TCellPhone = class(TComputer) 
public 
    constructor Create(Cup: Integer); overload; virtual; 
    constructor Create(Cup: Integer; Teapot: string); overload; virtual; 
end; 

это уже известно, что TCellPhone имеет три конструктора:

  • Cup: Integer
  • Cup: Integer; Чайник: строка
  • [чайник: String = '']

Как я изменить декларацию TCellPhone, чтобы скрыть конструктор предка? например так что:

TNokia = class(TCellPhone) 
end; 

будет иметь только два конструктора:

  • Cup: Integer
  • Cup: Integer; Чайник: строка

Теперь для случая, когда reintroduce используется для скрытия невиртуального предка. В предыдущем случае TiPhone имеет четыре конструктора (в идеале их было бы всего два - с TComputer каким-то образом скрывал своего предка). Но даже если я не могу исправить TComputer, я могу изменить TiPhone только иметь один:

TComputer = class(TObject) 
public 
    constructor Create(Teapot: string=''); 
end; 

TCellPhone = class(TComputer) 
public 
    constructor Create(Cup: Integer); overload; virtual; 
    constructor Create(Cup: Integer; Teapot: string); overload; virtual; 
end; 

TiPhone = class(TCellPhone) 
public 
    constructor Create(Cup: Integer); reintroduce; 
end; 

Теперь TiPhone имеет только один конструктор:

  • Cup: Integer

реинтродукции обычно используется только для подавления предупреждения о скрытии виртуальных предков. В этом случае:

Create(Teapot: string = '') 

не является виртуальным - но я все еще могу использовать его, чтобы скрыть его.


Но Теперь, если я добавить еще перегружен в TiPhone:

TiPhone = class(TCellPhone) 
public 
    constructor Create(Cup: Integer); reintroduce; overload; 
    constructor Create(Handle: String); overload; 
end; 

Затем внезапно (ранее скрытые) предки возвращаются:

  • TiPhone.Create (7) ;
  • TiPhone.Create ('pink');
  • TiPhone.Create (7, 'pink');
  • TiPhone.Создайте();

Как вы можете видеть, я изо всех сил пытаюсь понять логику

  • когда что-то скрыто
  • как скрыть что-то
  • когда что-то показано
  • как показать что-то
+0

@Ian: Вы действительно задаете много вопросов сегодня! Ницца! –

+0

Вы действительно говорите, что можете скомпилировать run 'obj: = TCellPhone.Create ('foo')'? Я нахожу это удивительным. –

+0

Я, конечно, выполнил полный пример, используя это определение. К счастью, у нас уже есть ответ ниже. –

ответ

6

Вы не используете reintroduce, чтобы скрыть метод класс cestor. Вы делаете это просто, объявив метод с тем же именем, что и один в классе предков, без переопределения или перегрузки. Вы используете reintroduce для подавления предупреждения , который Delphi поднимает, когда метод класса предка (тот, который скрыт) является виртуальным.

Если метод потомка переопределяет предка, то он не скрывается. Призывы к методу предка направляются вместо потомка.

Если метод потомка перегружает предка, то он также не скрывается. Оба доступны для звонка.

+0

В случае перегрузки виртуального метода существует специальный случай. Вы можете использовать комбинированное 'reintroduce; перегрузки; '. Это, похоже, не соответствует вашим объяснениям, так как голая «перегрузка» вызывает предупреждение. –

+0

Ответьте, что сказал Мухаммад, вы можете расширить свой ответ, Роб, чтобы объяснить, что происходит в этом случае (случай, когда добавление 'reintroduce' * используется *, чтобы скрыть метод класса предков)? –

+0

Боюсь, я не могу, @ Я. Это потребует от меня экспериментировать, и у меня нет компилятора Delphi для этого. Кто-то еще должен будет перечислить все различные способы, которыми эти директивы могут использоваться вместе, и какие эффекты имеют (или не имеют). –

1

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

Редактировать: Я бы отозвал свое утверждение «Вы ничего не скрываете». Я думаю, что я не совсем понимаю смысл скрываться здесь. Я спросил об этом quesiton.

обновление:
Основываясь на answer меня, я хотел бы повторно фразу мой ответ: Поскольку TComputer.Constructor не объявлен виртуальным, вы уже скрылось этот метод из классов-потомков. Таким образом, конструкторы TCellPhone не могут скрыть то, что не было видно вообще, поэтому никаких предупреждений компилятора.

+0

Это правильный ответ. В частности, конструктор «TComputer» должен быть виртуальным, чтобы запускать предупреждения. –

+0

Но Sertac, о чем говорит Иан, что 'TComputer.Create' * не скрыт в этом случае. Он может вызывать 'TCellPhone.Create ('foo')', а код компилируется и запускается, создавая объект 'TCellPhone'. Он предложил сделать снимок экрана, чтобы доказать это. –

+0

@Rob - Я спросил [вопрос] (http://stackoverflow.com/questions/3878576) о том, что означал компилятор с * hiding *. Дело не в том, что вы вообще не можете позвонить, это означает, что вы не сможете переопределить «TComputer.Constructor» у потомков «TCellPhone». Пожалуйста, см. [Ответ Кена] (http://stackoverflow.com/questions/3878576/method-s-hides-virtual-method-of-base-type-s-whats-really-being-hidden/3878637#3878637) и Комментарий Барри. –

0

Я хотел поставить это как комментарий к ответу Роба Кеннеди, но так как я не могу, здесь я иду ..

все время нет никакого предупреждения о скрытии предку конструкторами.

Просто потому, что вы этого не сделаете.

Если я скрываю предка, почему нет предупреждения? я не возвращаю.

Опять же, просто потому, что вы ничего не скрываете.

Вы видели доказательство того, что ничего не скрывали. Это 3 доступных конструктора, которые вы проверили, является доказательством.

Почему можно повторно использовать, чтобы скрыть предка?

Как упоминалось в Rob, повторное введение просто подавляет подсказку/предупреждение компилятора. За этим словом нет никакой реальной технической поддержки. Таким образом, вы не скрываете ничего с повторным введением.

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

Редактировать: Я просто прочитал сообщения, которые вы упомянули, я думаю, вы неправильно поняли понятия. Вот мое короткое объяснение.

reintroduce is used to hide ancestor constructors

Ответ этот пост не означает, что. Тот, кто действительно скрывает конструктор предка, является НОВЫМ КОНСТРУКТОРОМ потомка с тем же параметром с предком. Ключевое слово reintroduce просто подавляет предупреждение компилятора.

reintroduce is used to show ancestor constructors

В ответ на этот пост, это ПЕРЕГРУЗКИ ключевое слово, которое делает конструктор предка еще доступны.

Сложение в ответ на вопрос Яна в своем комментарии ниже:

Первый шаг, чтобы решить путаницу, чтобы определить реальные проблемы. Если мы внимательно рассмотрим ваши сообщения, станет очевидно, что вы на самом деле хотели решить две проблемы за один шаг. Эти две проблемы:

  1. Чтобы скрыть предок конструктор с определенным именем
  2. Чтобы иметь несколько конструктор с тем же определенным именем в потомка.

Хотя они могут показаться простыми проблемами, но тщательный осмотр сразу же ударит вас по голове, чтобы их натуры были точно противоположны друг другу. Проблема 1 хочет скрыть метод/конструктор, в то время как проблема 2 хочет показать не только один, но и несколько методов/конструкторов. Поэтому, если вы смешаете их вместе за один шаг, они обязательно отменяют друг друга. Неудивительно, что они дают вам головную боль ... :)

Основное правило для решения этих двух проблем - не смешивать их в один шаг. Это означает, что нам нужен промежуточный класс для решения проблемы 1 и перегрузка потомков этого промежуточного класса. Что-то вроде этого:

type 
    TComputer = class(TObject) 
    private 
    FCaller: TConstructorCaller; 
    public 
    constructor Create(Teapot: string=''); virtual; 

    property Caller: TConstructorCaller read FCaller; 
    end; 

    TBaseCellphone=class(TComputer) 
    constructor Create(Cup: Integer); virtual; 
    end; 

    TCellPhone = class(TBaseCellphone) 
    protected 
    public 
    constructor Create(Cup: Integer); overload; override; 
    constructor Create(Cup: Integer; Teapot: string); overload; virtual; 
    end; 

    TiPhone = class(TCellPhone) 
    public 
    constructor Create(Cup: Integer); reintroduce; overload; 
    constructor Create(Handle: String); reintroduce; overload; 
    end; 

Из вышеуказанного кода TBaseCellphone является промежуточным классом. В этом случае его задача состоит только в том, чтобы скрыть конструктор Create TComputer. Обратите внимание, что вы НЕ ДОЛЖНО использовать ключевое слово перегрузки здесь, или скрытие будет отменено. Теперь, после того, как скрытие завершено, теперь вы можете свободно спамить ваше ключевое слово перегрузки в своих потомках, чтобы получить несколько конструкторов с тем же именем.

Чтобы проверить, вы можете увидеть, что следующий код не будет компилироваться:

TCellPhone.Create('My Teapot'); 
+0

Я думаю, что нашел ошибку в Delphi. Ключевое слово 'overload' делает конструктор предка все еще доступным - если это не то, для чего оно предназначено. Он предназначен для разрешения двух перегрузок в «TCellPhone». Как я могу использовать перегрузку, чтобы допускать множественные перегрузки, но не допускать видимость методов от предка? IOW: Как скрыть конструктор предка? –

+0

@Ian - Это не будет ошибкой. [Методы перегрузки] (http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devcommon/methods_xml.html#4F7665726C6F6164696E67204D6574686F6473): * «... если метод redeclared имеет другую сигнатуру параметра от его предка, он перегружает унаследованный метод, не скрывая его ». * –

+0

Ian, перегрузка позволяет вам иметь несколько подпрограмм или методов с тем же именем. Им нужно иметь только разные параметры. Что касается вашего вопроса, я посмотрю, смогу ли я придумать хороший пример. – Luthfi

1

Ну, кажется, вы не можете скрыть метод/конструктор в классе, где вы перегружать его. Я пришел с этим маленьким «взломать», чтобы суметь скрыть конструктор из TComputer

TComputer = class(TObject) 
    public 
     constructor Create(Teapot: string=''); 
    end; 

    THackComputer = class(TComputer) 
    public 
    constructor Create(Cup : Integer);virtual; 
    end; 

    TCellPhone = class(THackComputer) 
    public 
     constructor Create(Cup: Integer); overload; override; 
     constructor Create(Cup: Integer; Teapot: string); overload; virtual; 
    end; 

    TiPhone = class(TCellPhone) 
    public 
    constructor Create(Cup: Integer); reintroduce; virtual; 
    end; 

В этом Exemple, TiPhone будет иметь только 1 конструктор доступен. Однако он нарушает полиморфизм (цена, которую нужно заплатить, чтобы скрыть второй конструктор от TCellPhone). Я хотел бы знать, нашел ли кто-нибудь способ сделать это, не нарушая полиморфизм.

Также обратите внимание, что это не потому, что проницательность кода показывает вам 4 «конструктора», которые действительно доступны 4. Я отметил, что для каждого «переопределения» конструктора я бы имел 1 конструктор, указанный в проницательности кода. Но в этой ситуации будет вызываться только конструктор потомков.

Этот пример будет жаловаться на то, что 2-й конструктор TCellPhone скроет один из THackComputer, но я думаю, что это ложный позитив, поскольку тот из THackComputer переопределен в TCellPhone. (Ошибка Корневого случая, я думаю, поскольку это не очень распространенная структура кода)

+0

Это очень аккуратный трюк. я получаю ту же самую проблему (жалуюсь, что другой конструктор в «TCellPhone» скрывает тот, что у «THackComputer»). Объяснение, почему он жалуется, - это то, что заслуживает того, чтобы его спросили - в пятом вопросе о конструкторах в Delphi. Причина состоит в том, что * я не понимаю, почему она жалуется *, и это всегда возможность учиться. –

+0

Ну, как я уже сказал, это может быть просто ошибка в углу. Некоторая структура кода обманывает компилятор в предоставлении «ложных положительных» предупреждений время от времени. –