Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

проблема с виртуальными функциями

29K
02 июля 2008 года
John Paramol
22 / / 08.04.2008
Привет.
Столкнулся с небольшой проблемой, касательно виртуальных функций.
Стыдно спрашивать, ибо это нечто элементарное.
Просто я уже успел подзабыть основы ООП в C++.

Вот пример.

Код:
#include <stdio.h>

class A
{
  public:
    void Func()
    {
      VirtFunc();
    }
    ~A()
    {
      Destroy();
    }
    virtual void VirtFunc()
    {
      puts("A::VirtFunc() called");
    }
    virtual void Destroy()
    {
      puts("A::Destroy() called");
    }
};

class B : public A
{
  public:
    virtual void VirtFunc()
    {
      puts("B::VirtFunc() called");
    }
    virtual void Destroy()
    {
      puts("B::Destroy() called");
    }
};

int main()
{
  {
    B BObject;
    BObject.Func();
  }
  getchar();
  return 0;
}


В методе Func вызывается виртуальная функция VirtFunc из наследника - так и надо.
Но в деструкторе наследника почему-то вызывается виртуальная функция Destroy из родителя.
Мне нужно при разрушении наследника вызвать Destroy наследника.
Где я туплю?
361
02 июля 2008 года
Odissey_
661 / / 19.09.2006
Ну так наверно ж надо сделать виртуальный деструктор ? =) Где он у вас вообще у класса B ?
29K
02 июля 2008 года
John Paramol
22 / / 08.04.2008
Мне он не нужен у класса B.
Класс B наследует деструктор класса A.
Но в нем, по идее, должен вызываться метод B::Destroy(), т.к. он виртуальный. Но вызывается A::Destroy()...
255
02 июля 2008 года
Dart Bobr
1.4K / / 09.04.2004
Цитата: John Paramol
Мне он не нужен у класса B.
Класс B наследует деструктор класса A.
Но в нем, по идее, должен вызываться метод B::Destroy(), т.к. он виртуальный. Но вызывается A::Destroy()...



Читать Мейерза до полного просветления!!
З.Ы. И деструктор сделать виртульным.

3
02 июля 2008 года
Green
4.8K / / 20.01.2000
Как в конструкторе, так и в деструкторе невозможно вызвать виртуальные ф-ции. Точнее, они будут вызваны не как виртуальные.
И на то есть веская причина: в момент когда вызывается деструктор базового класса (A) содержимое производного класса (B) уже разрушено и вызов виртуальной ф-ции может привести к обращению к уже разрушенным данным. Аналогично и для конструктора,- когда вызывается конструктор базового класса, содержимого производного класса ещё не существует.

P.S. Если используешь виртуализацию, деструктор надо делать виртуальным. Это правило хорошего стиля.
29K
02 июля 2008 года
John Paramol
22 / / 08.04.2008
Dart Bobr, написал бы просто "не знаю".
Green, спасибо за единственно верный ответ. Про виртуальный деструктор я знаю, просто старался пример упростить.
255
02 июля 2008 года
Dart Bobr
1.4K / / 09.04.2004
Цитата: John Paramol
Dart Bobr, написал бы просто "не знаю".
Green, спасибо за единственно верный ответ. Про виртуальный деструктор я знаю, просто старался пример упростить.



Конечно не знаю!! Не знаю, как заставить вас книги читать, а не задавать тут давно описаные вопросы. И кажется, довольно ясно написал - читай Мейерза!! Написал бы ясно - "не умею униги читать". В 55 эффективных решений для С++ описан твой вопрос от А до Я.

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

29K
03 июля 2008 года
John Paramol
22 / / 08.04.2008
Dart Bobr
Признаюсь, Мейерза не читал.
Зато читал Либерти, Саттера, Страуструпа, Уэллина, даже Кернигана с Риччи, еще каких-то.
Но это было лет 5 назад. В то время я мог похвастаться, что знаю C++ от А до Я.
Но со временем все забываешь (если, конечно, не занимаешься этим постоянно).

А что плохого, если форум помогает человеку получить быстрый ответ не переворачивая всю домашнюю библиотеку?
Это одна из его прямых функций.

Так что давайте жить дружно


P.S.
А я придумал один изврат. Надо написать базовый класс на обжект паскале. Там порядок вызовов конструкторов/деструкторов обратный, следовательно, не должно быть такого ограничения.

Если серьезно, то это довольно хреновый минус языка.
3
03 июля 2008 года
Green
4.8K / / 20.01.2000
Цитата: John Paramol
Dart Bobr
Если серьезно, то это довольно хреновый минус языка.


Если ты про то, что из деструктора нельзя вызывать виртуальные функции, то это не минус языка, а скорее плюс. Точнее даже, что это просто логичная вещь.
Ведь то, что от перестановки мест слагаемых сумма не изменяется, ты же не считаешь хреновым минусом математики.

Я не знаю паскаль, но что-то мне подсказывает, что и в этом языке нельзя (некорректно) обращаться к членам ещё несозданных или разрушенных объектов.

И я уверен, что твой "изврат" можно реализовать другими способами, нежели вызовом виртуальной ф-ции из деструктора. Расскажи про задачу и мы поможем, чем сможем.

255
03 июля 2008 года
Dart Bobr
1.4K / / 09.04.2004
Цитата: John Paramol
Dart Bobr
Признаюсь, Мейерза не читал.
Зато читал Либерти, Саттера, Страуструпа, Уэллина, даже Кернигана с Риччи, еще каких-то.


Это хорошо, но С++ развивается постоянно.

Цитата: John Paramol
Dart Bobr
А что плохого, если форум помогает человеку получить быстрый ответ не переворачивая всю домашнюю библиотеку?
Это одна из его прямых функций.


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

Цитата: John Paramol
Dart Bobr
Так что давайте жить дружно


А я и не ссорился ни с кем.

Цитата: John Paramol
Dart Bobr
P.S.
А я придумал один изврат. Надо написать базовый класс на обжект паскале. Там порядок вызовов конструкторов/деструкторов обратный, следовательно, не должно быть такого ограничения.


Я не писал на обжект паскале, но мне кажется очень подозрительным, то что вы говорите. Как можно инициализировать дочерний объект, если в нем не инициализированы родительские поля? Это с точки зрения логики чревато ошибками.

5
03 июля 2008 года
hardcase
4.5K / / 09.08.2005
Цитата: Dart Bobr
Как можно инициализировать дочерний объект, если в нем не инициализированы родительские поля? Это с точки зрения логики чревато ошибками.


В ObjectPascal нужно руками вызывать метод предка (а также конструктор и деструктор, так как это обычные методы с т.з. языка) в самом теле метода, при том в любом месте, отсюда и "обратный" порядок вызовов.

29K
03 июля 2008 года
John Paramol
22 / / 08.04.2008
Цитата: Dart Bobr
Но, никто не станет цитировать в сотый раз специально для вас книгу. Согласитесь - это скучная работа, которая никоим образом не развивает участников форума.


Согласен.
Ладно, теперь вопрос плавно перетекает в проблему реализации.
Вот простой пример того, что мне нужно сделать (пример нерабочий):

Код:
class A
{
    TMySuppaList List;
  public:
    // ...
    virtual void ClearList() = 0;
    virtual ~A()
    {
      ClearList(); // имеется в виду уже перегруженный в наследнике
    }
};

class B: public A
{
  public:
    // ...
    virtual void ClearList()
    {  
      // очистка List
      // для каждого наследника будут разные действия
    }    
};


Метод ClearList будет часто использоваться сам по себе
3
04 июля 2008 года
Green
4.8K / / 20.01.2000
Цитата: John Paramol
Согласен.
Ладно, теперь вопрос плавно перетекает в проблему реализации.
Вот простой пример того, что мне нужно сделать (пример нерабочий):

Код:
class A
{
    TMySuppaList List;
  public:
    // ...
    virtual void ClearList() = 0;
    virtual ~A()
    {
      ClearList(); // имеется в виду уже перегруженный в наследнике
    }
};

class B: public A
{
  public:
    // ...
    virtual void ClearList()
    {  
      // очистка List
      // для каждого наследника будут разные действия
    }    
};


Метод ClearList будет часто использоваться сам по себе



Почему List объявлен в родителе, а очищается в наследнике?
Да и как наследник получает к нему доступ.
Правильнее будет сделать так, чтоб в каком классе данные объявлены, тот класс и оперировал им. Называется это ИНКАПСУЛЯЦИЯ - один из трех китов ООП.

Ну даже если уж очень надо сделать так, то зачем делать это через задний проход? :)
Если можно значительно проще и очевиднее:

Код:
class A
{
    TMySuppaList List;
  public:
    // ...
    virtual void ClearList() = 0;
};

class B: public A
{
  public:
    virtual ~B()
    {
      ClearList(); // имеется в виду уже перегруженный в наследнике
    }

    // ...
    virtual void ClearList()
    {  
      // очистка List
      // для каждого наследника будут разные действия
    }    
};
87
04 июля 2008 года
Kogrom
2.7K / / 02.02.2008
Можно конечно не использовать деструктор в наследнике, если немного извратиться, но это уже юмор и трюкачество.

Код:
class A
{
public:
    void (*p)();
    A()
    {
        p = 0;
    }
    virtual ~A()
    {
        cout << "~A" << endl;
        (*p)();// может указывать на статическую функцию наследника
    }
};

class B: public A
{
public:
    static void ClearList()
    {
        cout << "ClearList" << endl;
    }
    B()
    {
        p = ClearList;
    }
};

int main()
{
    B b;
    return 0;
}
36K
04 июля 2008 года
Alno
34 / / 23.06.2008
Цитата: John Paramol
Dart Bobr
P.S.
А я придумал один изврат. Надо написать базовый класс на обжект паскале. Там порядок вызовов конструкторов/деструкторов обратный, следовательно, не должно быть такого ограничения.



Мне кажется, или для решения проблемы без изврата достаточно сделать две вещи: сделать конструктор виртуальным и переопределить его в наследнике, вызвав Destroy там?

29K
06 июля 2008 года
John Paramol
22 / / 08.04.2008
Спасибо за помощь, я разобрался. Пересмотрел всю логику и сделал переразложение на классы, так, что теперь ничего "обходить" не надо.
И сделал для себя кое-какой вывод:
если на каком-то этапе разработки видится необходимость в изврате, обхождении препятствий, обусловленных правилами языка, то проблема скорее всего в неправильном выстраивании бизнес-логики.
3
06 июля 2008 года
Green
4.8K / / 20.01.2000
Цитата: John Paramol

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


Очень правильный вывод.

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог