- •Министерство образования российской федерации Воронежский государственный технический университет а.Г. Остапенко г.А. Кащенко и.В.Давыдов Морев д.Е.
- •Воронеж 2001
- •Рецензенты: Остапенко г.А.
- •Введение
- •Методы разработки программного обеспечения
- •Подходы к разработке программного обеспечения
- •Планирование разработки программного обеспечения
- •Основные типы языков программирования.
- •Процедурное программирование
- •Функциональное программирование
- •Логическое программирование
- •Объектно-ориентрованное программирование
- •Процедуры.
- •Модули.
- •Абстрактные типы данных.
- •Построение программного обеспечения по объектно-ориентированной методике
- •2.1. Функционирование объектно-ориентированного программного обеспечения
- •2.2. Классы. Отношения между классами
- •Этапы построения программного обеспечения
- •2.4. Объектно-ориентированный анализ
- •Информационные модели
- •Жизненные циклы
- •Модели процессов
- •2.5. Нотация для объектно-ориентированного проектирования
- •2.6. Объектно-ориентированное проектирование – ood
- •2.7. Заключительное замечание
- •Основные недостатки:
- •3. Средства объектно-ориентированного программирования
- •Средства объектно-ориентированного рограммирования Turbo-Pascal
- •Понятие “объект”
- •Статические и виртуальные методы. Полиморфизм Статические методы
- •Виртуальные методы. Полиморфизм
- •Конструкторы и деструкторы
- •3.1.5. Сравнимость данных типа объект
- •3.1.6. Динамический вызов объектов
- •3.2. Средства объектно-ориентированного
- •Понятие “класс”
- •Компоненты классов. Доступ к ним.
- •Дружественные функции
- •Конструкторы и деструкторы
- •Статические члены классов
- •3.2.6. Перегрузка операций
- •3.2.7. Виртуальные функции
- •3.2.8. Динамическое создание объектов
- •3.2.9. Проверьте свои знания!
- •Литература:
- •Оглавление
- •Воронежский государственный технический университет,
- •394026 Воронеж, Московский просп. 14
3.2.7. Виртуальные функции
Пример 1. Виртуальные функции позволяют порожденным классам опредлять различные версии функций базового класса (переопределить их; не путать с перезагрузкой, рассмотренной в предыдущем параграфе!). Как известно, в С++ функции-члены класса с различным числом и/или типом параметров есть действительно разные функции, даже если они имеют одно имя. Виртуальные функции позволяют переопределять в порожденном классе функции, введенные в базовом классе, даже если число и тип аргументов те же самые. Для виртуальных функций нельзя переопределять тип функций. Виртуальная функция обязательно является членом класса и не может быть статическим членом класса. Конструктор не может быть виртуальным, виртуальный деструктор допускается.
class B
{
virtual void vf1 ();
virtual void vf2 ();
virtual void vf3 ();
void f ();
…….};
class D :public B
{
virtual void vf1 (); //виртуальная функция
void vf2 (int); //невиртуальная функция
//другой список аргументов
char vf3 (); //ошибка, изменен тип функции
};
void exf (); //внешняя функция
main ()
{ D d; //объявление объекта d класса D
B bp=&d; //стандартное преобразование из B* в B*
bp->vf1 (); //вызов D::vf1
bp->vf2 (); // вызов B::vf2
bp->f (); // вызов B::f
}
Спецификатор virtual в описании функции vf () класса D не обязателен: он присваивается автоматически, если в порожденном классе переопределяется функция базового класса (совпадает состав параметров). Если класс D, порожденный B, содержит функцию vf1 () того же самого типа, то vf1 () может быть вызван для объекта d класса D (D::vf1 ()) с использованием указателя или ссылки на В.
Пример 2.
#include <stdio.h>
class Base
{
public:
virtual void virt() // виртуальная функция
{
printf(“Hello from base Virt \n”);
}
void nonVirt() // невиртуальная функция
{
printf(“Hello from Base nonVirt \n”);
} };
class Derived :public Base
{
public:
void virt() //переопределение виртуальной функции
//базового класса
{
printf(“Hello from base Virt \n”);
}
void nonVirt()
{
printf(“Hello from Base nonVirt \n”);
} };
int main(void)
{
Base *bp=new Derived;
bp->virt(); //вызывается virt() класса Derived, потому что
//значением указателя bp является адрес Derived
bp->nonVirt(); //вызывает nonVirt() класса Base, потому что
//указатель bp по определению связан с Base
return 0;
}
Пример 3. Приведем в этом примере одну и ту же программу в двух вариантах (с использованием виртуальных методов и без них).
Без виртуальных методов.
#include<conio.h>
#include<iostream.h>
class Location
{
protected:
int X,Y;
public:
Location(int X1, int Y1);
void Position (int NX, int NY);
};
Location::Location(int X1, int Y1)
{ X=X1;
Y=Y1; }
void Location::Position(int NX, int NY)
{X=NX;
Y=NY; }
class Ch::public Location
{
protected:
char C;
public:
Ch(char C1, int i, int k):Location (i,k) {C=C1};
/*virtual*/ void Show(void);
void SetC(char NC);
void MoveTo(int NX, int NY);
};
void Ch::Show()
{
cprintf(“%c”,C);
getch();
}
void Ch::SetC(char NC)
{ C=NC; }
void Ch::MoveTo(int NX, int NY)
{ X=NX;
Y=NY;
clrscr();
gotoxy(NX,NY);
Show();
}
class St::public Ch
{
protected:
char S;
public:
St(char S1, int j, int i, char C1):Ch(C1,i,j)
{ S=S1; };
/*virtual*/ void Show(void);
{ void SetS(char S2); };
void St::Show()
{ cout<<S; };
void St::SetS(char S2)
{ S=S2; };
int main()
{St MySt(‘a’,10,15,’b’); //A
St *Ptr=&MySt; //B
Ptr->SetS(‘c’); //C
Ptr->MoveTo(5,10); //D
return 0;
}
В строке //A объявляется объект MySt класса St, а это означает запуск конструктрора с заданными значениями параметров. В конструкторе St вызывается конструктор Ch, а из него, в свою очередь, конструктор Location. В результате переменные получат следующие значения:
S=’a’; C=’b’; X=10; Y=15;
В строке //В объявляется указатель Ptr на объект класса St и ему присваивается адрес MyStr. В результате выполнения //C переменная S=’с’. В строке //D предусмотрен вызов метода MoveTo(), которого нет в классе St. Поэтому поск этого метода продолжается в классе-предшественнике Ch и запускает метод Ch::MoveTo. В нем имеется вызов метода Show(), но имеется два метода под этим названием Ch::Show и St::Show. Так как в ходе выполнения программы был выполнен переход в класс Ch, то запускается Ch::Show() и на экране появится символ ‘b’.
С виртуальными методами.
#include<conio.h>
#include<iostream.h>
class Location
{
protected:
int X,Y;
public:
Location(int X1, int Y1);
void Position (int NX, int NY);
};
Location::Location(int X1, int Y1)
{ X=X1;
Y=Y1; }
void Location::Position(int NX, int NY)
{X=NX;
Y=NY; }
class Ch::public Location
{
protected:
char C;
public:
Ch(char C1, int i, int k):Location (i,k) {C=C1};
virtual void Show(void);
void SetC(char NC);
void MoveTo(int NX, int NY);
};
void Ch::Show()
{
cprintf(“%c”,C);
getch();
}
void Ch::SetC(char NC)
{ C=NC;
void Ch::MoveTo(int NX, int NY)
{ X=NX;
Y=NY;
clrscr();
gotoxy(NX,NY);
Show();
}
class St::public Ch
{
protected:
char S;
public:
St(char S1, int j, int i, char C1):Ch(C1,i,j)
{ S=S1; };
virtual void Show(void);
{ void SetS(char S2); };
void St::Show()
{ cout<<S; };
void St::SetS(char S2)
{ S=S2; };
int main()
{St MySt(‘a’,10,15,’b’);
St *Ptr=&MySt;
Ptr->SetS(‘c’);
Ptr->MoveTo(5,10);
return 0;
}
Программа выполняется аналогично предыдущей, за исключением метода MoveTo. Благодаря виртуальности вызывается St::Show() и на экране появится символ ‘c’.
Пример 4. Модифицируем пример, приведенный в п. 3.2. Если внимательно посмотреть на функцию Point::MoveTo и Circle::MoveTo, то не трудно видеть, что они совпадают, разница только в том, какие функции Hide() и Show() они вызывают (для объектов класса Point или Circle). Использование виртуальных функций позволяет решить эту проблему и исключить функцию Circle::MoveTo. Аналогичный пример на Turbo Pascal дан в п. 3.4. Для чего функциям Show() и Hide() в классах Point и Circle добавим атрибут virtual и исключим функцию MoveTo из класса Circle. Предоставим это сделать читателю самостоятельно.