Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Виртуальные функции  (Прочитано 23564 раз)
0 Пользователей и 9 Гостей смотрят эту тему.
Guest
Гость
« : 18-01-2004 20:17 » 

Доброго времени суток !!!

Мой вопрос - виртуальные ф-ции
Что это такое? Т.е если ф-ция член класса объявлена с ключевым словом virtual. Для себя я понял, что это значит что функция с таким же именем в производном классе будет заменять данную ф-цию в базовом классе.

class A
{
public:
   virtual void PrintString()
   {
       printf("string from class A %s", s);
    }
};

class B public: A
{
   ...
   ...
   ...
public:
   void PrintString()
   {
       printf("string from class B %s", s);
    }

}

Т.е если я сздаю экземпляр класса B то

B obj;
obj.PrintString; - будет вызвана ф-ция из класса B, а не одноимённая
ф-ция из базового класса A.

Так ли это? И как можно вызвать PrintString() из класса A?
Записан
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 18-01-2004 21:13 » 

Смысл виртуальных ф-ий в том, что они задают "интерфейс" базового класса, который остается при наследовании. Если была у тебя ф-ия PrintString, то она у тебя и останется, но ее можно переопределить.
Кроме того, есть еще одно полезное свойство витуальной ф-ии с использованием указателя базового класса.
Код:
#include <stdio.h>

class c1 {
public:
int x;
virtual void set(int n) | x=n; }
};

class c2 : public c1 {
public:
int y;
void set(int n) | y=n; }
};

int main() {
c1 *p1;
c2 *p2;

p2=new c2;
p1=p2; /* указателю базового класса присваивается указатель на производный класс */
p2->x=p2->y=0; /* на всякий случай обнуляем - для контроля*/
p1->set(10); /* вызывается текущая вирт-ая ф-ия, а именно c2::set() */
p1->c1::set(20); /* вызов c1::set() */
printf("%d %d\n",p2->x,p2->y); /* проверяем, правильно ли мы тут понаписали */
return 0;
}
Все это подробно описано в любой книге по С++.
Кроме того, не так давно это обсуждалось тут.
« Последнее редактирование: 23-11-2007 18:49 от Алексей1153++ » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Guest
Гость
« Ответ #2 : 18-01-2004 21:54 » 

Цитата

p1=p2; /* указателю базового класса присваивается указатель на производный класс */

 
А для чего это? И корректно ли это выражение, ведь p1 и p2 указатели разных типов?
Записан
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #3 : 18-01-2004 22:42 » 

Вот об этом я и говорил: "полезное свойство витуальной ф-ии с использованием указателя базового класса". Указателю базового типа можно присваивать адрес объекта производного типа. Через этот указатель доступны только члены и ф-ии определенные в базовом классе, но ф-ия, объявленная виртуальной, будет вызвана из производного класса.
Почитай литературу - в сети этого добра навалом.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Guest
Гость
« Ответ #4 : 28-01-2004 05:00 » 

RXL - СПАСИБО! скачал книжку-прочитал-разобрался, но два момента по прежнему не дают покоя (((
1. Где находится таблица виртуальных ф-ций - в области памяти (фрагменте) базового класса (объекта) или она расположена во фрагменте производного класса(объекта), а где находится указатель на неё?
2. Как когда и где необходимо объявлять методы класса виртуальными. Т.е когда это имеет смысл. Какими соображениями надо руководствоваться при выборе типа метеода вообще Не понял

И ещё буду очень благодарен за ответ на такой вопрос:
Как проектировать программу разрабатываемую на C++?
Я подумал, а если класов в ней сотни... Необходимо какето внешнее средство?
Заранее благодарен всем !!!
Записан
Alf
Гость
« Ответ #5 : 28-01-2004 07:41 » 

Цитата: Guest
RXL - СПАСИБО! скачал книжку-прочитал-разобрался, но два момента по прежнему не дают покоя (((
1. Где находится таблица виртуальных ф-ций - в области памяти (фрагменте) базового класса (объекта) или она расположена во фрагменте производного класса(объекта), а где находится указатель на неё?
Вопрос внутреннего строения классов с виртуальными функциями недавно обсуждался здесь: https://forum.shelek.ru/index.php/topic,2252.0.html
Цитата: Guest
2. Как когда и где необходимо объявлять методы класса виртуальными. Т.е когда это имеет смысл. Какими соображениями надо руководствоваться при выборе типа метеода вообще Не понял
Это имеет смысл, если базовый класс может иметь много ипостасей, т.е. производные от него объекты должны обладать полиморфизмом. Вкратце - если этот класс является базовым и его потомки должны иметь возможность при необходимости переопределить содержание его метода, сохранив при этом интерфейс в неприкосновенности.
Цитата: Guest
И ещё буду очень благодарен за ответ на такой вопрос:
Как проектировать программу разрабатываемую на C++?
Я подумал, а если класов в ней сотни... Необходимо какето внешнее средство?
Заранее благодарен всем !!!
Во-первых, методология, предложенная еще Дейкстрой (кстати, на днях должен выйти перевод его статьи, где этот вопрос - один из главных), развитая последователями и отшлифованная Гради Бучем и группой Rational в виде SADT.
Во-вторых, инструментальные средства поддержки SADT (например, Rose от Rational или AllFusion от Computer Associates).
Ну и, естественно, чувство меры...
Записан
Anonymous
Гость
« Ответ #6 : 28-01-2004 08:11 » 

<Это имеет смысл, если базовый класс может иметь много ипостасей, т.е. <производные от него объекты должны обладать полиморфизмом. Вкратце - <если этот класс является базовым и его потомки должны иметь возможность <при необходимости переопределить содержание его метода, сохранив при <этом интерфейс в неприкосновенности.

Т.е вкратце так: объект потомок адресуется указателем на объект базового класса. Появляется возможность выбора: вызов метода производного класса (по умолчанию) или вызов одноимённого метода объекта базового класса (что и означает термин "переопределение"). - тут я правильно понял?

Вот как я себе обрисовал ситуацию на примере простой очереди из объектов:
1. Есть базовый класс - задаёт основную функциональность для всех потомков.
2. Есть несколько потомков напрямую наследующих базовый класс. Они добавляют немного функциональности к уже имеющейся.
3. Есть клас объекта реализующего очередь FIFO.
Вот здесь у меня возник вопрос:
Какой тип указателя на объект надо прописывать в объявление метеода
CFIFO::AddItem(Не понял ptr) если в очередь будут добавлятся произвольные потомки базового класса. Т.е не делать же для каждого потомка свой CFIFO::AddItem(Не понял ptr)

Теперь, если я правильно всё понял, ответ на этот вопрос звучит просто - виртуальные функции!

Т.е метод класса реализуещего добавление элемента в очередь объявляется с типом указателя на базовый!!! (для всех помещаемых в очердь объектов) класс, а методы потомков объявляются виртуальными. Тогда указатель для всех объектов будет единым!!!, а главное что по нему можно будет вызвать методы конкретного потомка!

В этом я увидел практическое применение идеи виртуальных функций.
Теперь собственно вопрос: - правильно ли я всё понял Улыбаюсь
Буду благодарен всем кто поправит!
Записан
Guest
Гость
« Ответ #7 : 28-01-2004 08:22 » 

Как следует правильно понимать термины:
- переопределить метод базового класса;
- перекрыть метод базового класса;
Записан
Guest
Гость
« Ответ #8 : 28-01-2004 09:50 » 

class A
{
private:    
       int value;
public:
       virtual void SetValue(int param){value = param};
};

class B: public A
{
private:
       int adv_value;
public:
       SetValue(int param){adv_value = param*2);
};

main()
{
    A *pA = NULL;
    B *pB = new B;

    pA = pB;
    pA->SetValue(5); //вызов реально pB::SetValue - это называется переопре
                               // делением метода базового класса?
    pA->A::SetValue(5); //вызов витруального A::SetValue(5) из базового класса
}

Теперь переопределим метод B::SetValue(int) так:

void B::SetValue(int param)
{
   int res = A::SetValue(param);
   adv_value = res*2;
}

Т.е сначала отрабатывает метод родителя, получается результат, и выполняется требуемая "доводка" в методе потомка. - Может это называется
переопределением???

Я запутался Улыбаюсь)
Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #9 : 28-01-2004 12:12 » 

Я тоже запутался Ага

class A
{
public:
   A(){}
   virtual ~A(){}
   virtual const char * GetName() { return "1 Базовый класс\n";}
};

class B : public A
{
public:
   B(){}
   virtual ~B(){}
   virtual const char * GetName() { return "2 Производный класс\n";}
};

int _tmain(int argc, _TCHAR* argv[])
{
   A *p = new A();
   printf("%s",p->GetName());
        delete p;

        p = new B();
   printf("%s",p->GetName());
        delete p;

   return 0;
}

В данном случае функция GetName() была переопределена в производном классе. Те полностью вызывается другая функция, а базовой как бы не существует. Несмотря на то, что указатель типа базового класса, во втором случае произойдёт вызов функции из класса В.

Если всё же вызов родительской функции необходим (например часто это используется в функциях создания окошек), то это делается так:

class A
{
        virtual BOOL Create(...);
};

class B : public A
{
        virtual BOOL Create(...) { ..какие то действия..; return A::Create(...); }
};

Бываю случаи, когда функция обязана быть переопределена. Например, если базовый класс не содержит необходимых данных, тогда перегружаемая функция определяется как "чисто" виртуальная (pure virtual). В этом случае невозможно создание объекта производного класса, без переопределения такой функции.

class Figura
{
        // базовый класс понятия не имеет,
        // как должна быть нарисована каждая фигура
        virtual void Draw(...) = 0;
};

class Krug : public Figura
{
         virtual void Draw(...) { ..какие то действия..; }
};

class Kwadrat : public Figura
{
        // ошибка Draw(...) не переопределена
};

Есстественно, что вызов чисто виртуальных функций невозможен, как и невозможно создание объекта базового класса, содержащего такие функции.
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
Guest
Гость
« Ответ #10 : 28-01-2004 14:10 » 

Джон

Никак не пойму вот что:
1. Базовый класс:
class A
{
virtual BOOL Create(...);
};
--------------------------------
Производный класс:
class B : public A
{
virtual BOOL Create(...) { ..какие то действия..; return A::Create(...); }
};
Зачем в производном классе В ф-ция Create() описана как virtual?

2. Если не использовать указатель на базовый класс, а использовать нормалльный указатель:

B *p = new B();
printf("%s",p->GetName()); //вызов ф-ции из производного класса
printf("%s",p->A::GetName()); //вызов ф-ции из базового класса
delete p;

Тот же результат! Тогда зачем нужны виртуальные ф-ции?

3. Зачем (в твоём предыдущем примере) деструктор базового и производного  класса описан со спецификатором virtual ?


Джон,
Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #11 : 28-01-2004 15:18 » 

1.

В данном случае это было просто скопировано Ага , но представь себе:
class С : public В
{
virtual BOOL Create(...) { ..какие то действия..; return В::Create(...); }
};

и тд.

2. и 3. В принципе это большие теоретические вопросы, которые подробно описаны у Страуструпа. Поэтому я "некрасиво" отошлю тебя к нему, мы ведь не собираемся его тут пересказывать, а самое главное времени нет. И потом ежели уж ты решил программировать на С++, то от Страуструпа тебе не отвертеться Ага

Всё-таки коротко:
2.
Они нужны, чтоб облегчить жизнь программисту. Пример с Фигурой

представь у тебя штук 10 разных фигур тогда тебе надо создавать 10 разных объектов, надо всегда. Но что ты делаешь потом? Пишешь 10 строчек с вызовом Draw для каждого типа? Тоже можно, а если их у тебя 100, 1000 ?

Тогда ты делаешь список, но какого типа? Круг, квадрат? Нет - Фигура!

я не знаю знаешь ли ты STL, поэтому приведу псевдо код:

Объект список(фигура*) - список указателей на объект класса фигура

Добавление объектов

список.Добавить(new Круг);
список.Добавить(new Квадрат);
список.Добавить(new Треугольник);
...

теперь отрисовка всех объектов:

цикл для каждого элемента списка
{
      список.ПолучитьСледЭлемент()->Draw();
}

И всё. Каждый объект отрисует свою фигуру.

3.
А виртуальный деструктор, который тоже является по сути функцией, следит за "чистым" удалением объекта:

A *p = new B();
...
void Clear(A *p)
{
     delete p; //  в этом месте ф-я не знает какой объект был создан A или В
"виртуальность" деструктора позволяет смело использовать такой вызов.
}
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #12 : 13-04-2006 21:36 » 

(комент при перемещении: )
----------
см выше пост Альфа
https://forum.shelek.ru/index.php/topic,2167.msg38834.html#msg38834

раз имеется ссылка на уже встречавшееся (правда ссылка не работает), имхо кандидат в ЧАВО. Иначе - вернуть тему  взад
Записан

Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #13 : 14-04-2006 10:02 » 

ИМХО - это основы ООП - если это серьёзно - надо обращаться к литературе и учебникам. С этого начинать. А спрашивать если действительно трудности возникают. С ЧАВО тоже не надо увлекаться - не будем же мы вставлять туда вопросы типа что такое ООП? Что такое объект? В крайнем случае это темы статей.

про данную тему - не вижу ничего ценного - УДАЛЯТЬ.
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
Sel
Злобный
Администратор

ru
Offline Offline

« Ответ #14 : 14-04-2006 18:51 » 

Так может кто-нибудь займется статьями на эту тему?
Записан

Слово не воробей. Всё не воробей, кроме воробья.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #15 : 14-04-2006 19:10 » 

Sel, дак спрашивать надо в видимом форуме
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #16 : 22-04-2006 05:31 » 

Sel, Статья уже была, Михалыч писал. 

Удаляем тему?
Записан

Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #17 : 22-04-2006 07:06 » 

Повтор одного и того же несколько раз не вредит. Хотя маловероятно, что кто либо нырнет в тему двухлетней давности. Я за возрат темы.
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #18 : 22-04-2006 07:24 » new

Ну дык. Михалыч!
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines