Проблема с виртуальностью функций

Тема в разделе "LANGS.C", создана пользователем Wereww, 9 окт 2011.

  1. Wereww

    Wereww Дмитрий

    Публикаций:
    0
    Регистрация:
    13 июн 2009
    Сообщения:
    55
    Нежданно негаданно столкнулся с такой проблемой - решил переопределить ShowModal из второй формы и вызывать его уже с нужными параметрами, вот объявы второй формы:

    Unit2.h
    Код (Text):
    1. class TForm2 : public TForm
    2. {
    3. __published:    // IDE-managed Components
    4.         TButton *Button1;
    5.         TButton *Button2;
    6.         TBevel *Bevel1;
    7.         TLabel *Label1;
    8.         TLabeledEdit *LabeledEdit1;
    9.         void __fastcall Button2Click(TObject *Sender);
    10.         void __fastcall Button1Click(TObject *Sender);
    11. private:    // User declarations
    12. public:     // User declarations
    13.         __fastcall TForm2(TComponent* Owner);
    14.         virtual int __fastcall ShowModal(AnsiString ParentName, int ParentID, int Level);
    15. };
    Unit2.cpp
    Код (Text):
    1. int __fastcall TForm2::ShowModal(AnsiString ParentName, int ParentID, int Level)
    2. {
    3.  //SomeFunc(...)
    4.  
    5.  inherited::ShowModal();
    6. }
    Так вот, проблема появляется тогда когда я хочу вызвать именно ShowModal без параметров - тоесть
    Код (Text):
    1. Form2->ShowModal();
    Компилятор сразу же матерится на то, что существующая версия ShowModal переопределяет виртуальную, как поступить в этой ситуации ?

     
  2. Nafanya

    Nafanya Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    581
    А у Вас сигнатура для переопределяемого метода одинакова в производном и в базовом классе?

    Исходя из Ваших отрывков кода не ясна иерархия наследования, а именно:
    inherited::ShowModal(); inherited - что за класс? Базовый для TForm? Или как?
    TCustomForm - тоже не ясно, где находится данный класс в иерархии наследования.

    Покажите нам UML Class Diagram (Диаграмму Классов) по которой писалась реализация.

    Интуиция мне подсказывает, что ошибка допущена на стадии проектирования - ещё до написания кода.
     
  3. RedLord

    RedLord Member

    Публикаций:
    0
    Регистрация:
    23 июн 2005
    Сообщения:
    183
    Адрес:
    Ukraine
    как-то так

    class TForm2 : public TForm
    {
    public:
    using TForm::ShowModal();

    .....
    }
     
  4. Nafanya

    Nafanya Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    581
    RedLord
    Он переопределил метод ShowModal() в производном классе с другой сигнатурой ShowModal(AnsiString ParentName, int ParentID, int Level).

    В качестве результата получил сокрытие имён. Это ошибка проектирования.

    Использование using и перенаправляющих функций - это не выход. Лучше правильно проектировать с самого начала, чем потом исправлять такие ошибки.
     
  5. krabz

    krabz New Member

    Публикаций:
    0
    Регистрация:
    26 май 2010
    Сообщения:
    135
    Nafanya
    TForm - это не его класс, это из VCL C++ Builder. Не студия.
     
  6. RedLord

    RedLord Member

    Публикаций:
    0
    Регистрация:
    23 июн 2005
    Сообщения:
    183
    Адрес:
    Ukraine
    Nafanya

    не вижу связи.
     
  7. Nafanya

    Nafanya Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    581
    RedLord
    Есть установленный набор правил, которых придерживаются при разработке ПО. В данном случае нарушено правило 33 Мейерса, которое гласит: "Не скрывайте унаследованные имена". Источник - Scott Meyers, Effective C++

    То есть производный класс был изначально спроектирован некорректно, это привело к сокрытию имён из базового класса, о чём и говорит компилятор:
    hides - перевод: скрывает

    Вообщем вместо того чтобы переопределить виртуальную функцию базового класса ShowModal() в производном, он скрыл её методом ShowModal(AnsiString,int,int). Взгляните - сигнатуры у функций разные, о чём ещё можно спорить.
     
  8. Wereww

    Wereww Дмитрий

    Публикаций:
    0
    Регистрация:
    13 июн 2009
    Сообщения:
    55
    Изменить нет возможности - так как хидеры уже скомпилены Borland'om (ныне CodeGear) и сам код в bpl'ках, а дельфийские сорцы этих модулей ну нет никакого желания править, проще конечно по другому функцию обозвать - но есть же и другой способ - ведь это С++ а не С. Какие еще идеи ?
    Нафань, я честно не знаком, с UML по отношению к C++ Builder, может и есть такая приблуда в дельфе =)

    ЗЫ - На данный момент ограничился другим именем функции - чуть больше кода конечно - но принцип жмёт )
     
  9. Nafanya

    Nafanya Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    581
    Попробуйте применить композицию вместо наследования.
    Сделайте в классе-обёртке два интерфейсных метода:
    ShowModal(AnsiString ParentName, int ParentID, int Level)
    ShowModal()
    и вызывайте из них реальный ShowModal(), который выполняет всю работу.

    Что Вам мешает применить композицию?
     
  10. RedLord

    RedLord Member

    Публикаций:
    0
    Регистрация:
    23 июн 2005
    Сообщения:
    183
    Адрес:
    Ukraine
    Nafanya
    если я помню правильно, то как раз там он и рекомендует использовать using
     
  11. Nafanya

    Nafanya Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    581
    Ну примерно так, посмотрите подойдёт или нет для Вашего случая идиома композиции:
    Код (Text):
    1. class TForm2
    2. {
    3. TForm Object;
    4. public:      
    5. int __fastcall ShowModal(AnsiString ParentName, int ParentID, int Level)
    6. {
    7. //SomeFunc(...)
    8.  Object.ShowModal();
    9. }
    10.  
    11. int __fastcall ShowModal()
    12. {
    13.  Object.ShowModal();
    14. }
    15. };
     
  12. Nafanya

    Nafanya Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    581
    Майерс:
    "1)Имена в производных классах скрывают имена из базовых классов. При открытом наследовании это всегда нежелательно.
    2)Чтобы сделать скрытые имена видимыми, используйте using-объявления либо перенаправляющие функции."

    Только в особых случаях using и перенаправляющие функции, когда выхода другого нет.
     
  13. RedLord

    RedLord Member

    Публикаций:
    0
    Регистрация:
    23 июн 2005
    Сообщения:
    183
    Адрес:
    Ukraine
    Nafanya
    это он тоже писал?


    P.S. у ТС ситация описанная как раз в этом правиле.
    и для того, чтобы не нарушать семантику открытого наследования, нужно вывести наверх и перегруженные функции.
     
  14. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    540
    А не проще ли отказаться от попытки назвать функцию именно ShowModal, и назвать ее как-нибудь по-другому, более соответственно реальному положению вещей, скажем ShowCustomModal? так будете иметь и то, и другое, по необходимости. Зачем вам надо именно ShowModal ее назвать?
     
  15. Wereww

    Wereww Дмитрий

    Публикаций:
    0
    Регистрация:
    13 июн 2009
    Сообщения:
    55
    Именно моя проблема и не позволяет написать ни того ни другого

    Это VCL, базовые классы уже определены и то, что вы написали - работать не то, что будет...

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

    Так вот я и решил принципиально выяснить - есть ли такая возможность в С++ )
     
  16. Dmitry_Milk

    Dmitry_Milk Member

    Публикаций:
    0
    Регистрация:
    20 ноя 2007
    Сообщения:
    540
    Wereww, похоже ваша проблема решается так:

    Unit2.h
    Код (Text):
    1. class TForm2 : public TForm
    2. {
    3.    . . .
    4.         virtual int __fastcall ShowModal();
    5.         virtual int __fastcall ShowModal(AnsiString ParentName, int ParentID, int Level);
    6.    . . .
    7. };
    Unit2.cpp
    Код (Text):
    1.  . . .
    2. //---------------------------------------------------------------------------
    3. __fastcall TForm2::ShowModal()
    4. {
    5.   return TForm::ShowModal();
    6. }
    7. //---------------------------------------------------------------------------
    8. __fastcall TForm2::ShowModal(AnsiString ParentName, int ParentID, int Level)
    9. {
    10.    . . .
    11.   return TForm::ShowModal();
    12. }
    13. //---------------------------------------------------------------------------
    14.  . . .
    Так сразу и ворнинг исчезает, и название сохраняется, можно использовать оба варианта, выбирается корректно в зависимости от набора параметров.
     
  17. Wereww

    Wereww Дмитрий

    Публикаций:
    0
    Регистрация:
    13 июн 2009
    Сообщения:
    55
    Решение верное, так как ничего не остаётся как переопределять всё заново, ну одной строчкой кода больше, спасибо за солюху, а то голова ватная от этого проэкта :derisive: