Delphi: Когда вновь появляются шкуры предков и когда они показывают их? - PullRequest
6 голосов
/ 07 октября 2010

Сегодня Недавно в Stackoverflow я узнал, что:

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


Обновление: заменил весь вопрос:

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 конструктора:

  • Чашка: целое число
  • Чашка: целое число;Чайник: строка
  • [Чайник: 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 доступно четыре конструкторов:

  • Чашка: целое число
  • Чашка: целое число
  • Чашка: целое число;Чайник: строка
  • [Чайник: строка = '']

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


Повторное использование исходного кода:

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 имеет три конструктора:

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

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

TNokia = class(TCellPhone)
end;

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

  • Кубок: целое число
  • Кубок: целое число;Чайник: строка

Теперь для случая, когда 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.Создать («розовый»);
  • TiPhone.Create (7, «розовый»);
  • TiPhone.Create ();

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

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

Ответы [ 4 ]

7 голосов
/ 07 октября 2010

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

Если метод потомка переопределяет предка, значит, он не прячется.Вызовы метода предка вместо этого направляются на потомок.

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

1 голос
/ 07 октября 2010

Ну, кажется, вы не можете скрыть метод / конструктор в классе, где вы также перегружаете его.Я пришел с этим крошечным «хаком», чтобы скрыть конструктор от 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;

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

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

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

1 голос
/ 07 октября 2010

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

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

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

0 голосов
/ 07 октября 2010

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

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

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

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

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

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

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

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

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

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

Повторное введение используется для сокрытия конструкторов предков

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

reintroduce используется для показа конструкторов предков

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

ДОПОЛНЕНИЕ в ответ на вопрос Яна в его комментарии ниже:

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

  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 of TComputer.Обратите внимание, что вы НЕ ДОЛЖНЫ использовать ключевое слово перегрузки здесь, иначе скрытие будет отменено.Теперь, когда скрытие завершено, теперь вы можете свободно спамить ключевое слово перегрузки в его потомках, чтобы получить несколько конструкторов с одинаковыми именами.

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

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