Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lutsik_Yu_A_Obektno_orientir_programmir_na_yaz.pdf
Скачиваний:
63
Добавлен:
11.05.2015
Размер:
4.33 Mб
Скачать

{case '*':case '/':return 3; case '-':case '+':return 2; case '(':return 1;

} return 0;

}

 

 

 

 

 

 

int main()

 

 

 

 

 

 

{ char a[80];

// исходная строка

 

 

 

 

 

cl cls;

 

 

 

 

 

Р

cout << "\nВведите выражение (в конце символ '='): ";

 

 

 

cin >> a;

 

 

 

 

 

 

cout << cls.ANALIZ(a) << endl;

 

 

 

 

 

}

 

 

 

У

 

В результате работы программы получим:

 

 

Г

И

Введите выражение (в конце символ '=') :

(a+b)-c*d=

 

ab+cd*-

 

 

Б

 

 

 

4.2. Виртуальные функции

 

 

 

 

а

 

 

 

Прежде чем коснуться самого применения виртуальных функций, необ-

ходимо рассмотреть такие понятия, как раннее и позднее связывание.

 

 

к

 

 

 

 

Во время раннего связывания вызывающий и вызываемый методы связы-

 

ет

 

омпиляции.

 

 

 

ваются при первом удобном случае, обычно при

 

 

 

При позднем связывании вызыва мого и вызывающего методов они не могут быть связаны во время компиляции. Поэтому реализован специальный

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

 

 

 

его

и вызывающего методов, когда вызов будет сделан фактически.

Очевидно, что скорость и эффек ивность при раннем связывании выше,

чем при использован

ни

тсвязывания. В то же время позднее связывание

поздн

 

пол

 

 

обеспечивает некоторую у версальность процесса связывания.

Один из основных пр нц пов объектно-ориентированного программиро-

вания предполагает ис

ьзование идеи «один интерфейс множество методов

и

 

 

 

 

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

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

Преимущество применения виртуальных методов заключается в том, что при этом используется именно механизм позднего связывания, который допус-

74

кает обработку объектов, тип которых неизвестен во время компиляции. Рассмотрим пример использования виртуальной функции.

#include "iostream" #include "iomanip" using namespace std; #include "string.h"

 

class grup

 

 

 

 

// базовый класс

 

 

 

 

{ protected:

 

 

 

 

 

 

 

 

 

Р

 

 

char *fak;

 

 

//

наименование факультета

 

 

 

 

 

 

 

long gr;

 

 

//

номер группы

 

И

 

public:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

grup(char *FAK,long GR) : gr(GR)

У

 

 

 

{

}

 

 

 

 

 

 

 

 

 

 

if (!(fak=new char[20]))

 

 

 

 

 

 

 

 

{ cout<<"ошибка выделения памяти"<<endl;

 

 

 

 

 

return;

 

 

 

 

 

 

Г

 

 

 

 

}

strcpy(fak,FAK);

 

 

 

Б

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

сса

 

 

 

 

 

~grup()

 

 

 

 

 

 

 

 

 

 

 

 

 

к

grup " << endl;

 

 

 

 

{ cout << "деструктор

л

 

 

 

 

 

}

delete fak;

 

е

 

 

 

 

 

 

 

 

 

// объявление виртуальной функции

 

 

 

т

 

 

 

};

virtual void see(void);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

class stud : public grup

 

 

 

// производный класс

 

 

{

char *fam;

 

 

 

//

фамилия

 

 

 

 

 

 

и

 

 

 

//

массив оценок

 

 

 

 

 

int oc[4];

 

 

 

 

 

 

 

public:

о

 

 

 

 

 

 

 

 

 

 

 

stud(char *FAK,long GR,char *FAM,int OC[]): grup(FAK,GR)

 

б

 

 

 

 

 

 

 

 

 

 

 

 

 

{ if (!(fam=new char[20]))

 

 

 

 

 

 

 

л{ cout<<"ошибка выделения памяти"<<endl;

 

 

Б

 

}

return;

 

 

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

и strcpy(fam,FAM);

 

 

 

 

 

 

 

 

 

 

for(int i=0;i<4;oc[i]=OC[i++]);

 

 

 

~stud()

{ cout << "деструктор класса stud " << endl; delete fam;

}

void see(void);

};

75

void grup::see(void)

// описание виртуальной функции

 

 

{ cout << fak << gr << endl;}

 

 

 

 

 

 

void stud::see(void)

//

 

 

 

 

 

 

{ grup ::see();

 

// вызов функции базового класса

 

 

cout <<setw(10) << fam << " ";

 

 

 

 

 

 

for(int i=0; i<4; cout << oc[i++]<<’ ’);

 

 

 

 

cout << endl;

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

Р

int main()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

{ int OC[]={4,5,5,3};

 

 

 

 

 

И

grup gr1("факультет 1",123456), gr2("факультет 2",345678), *p;

 

stud st("факультет 2",150502,"Иванов",OC);

 

 

 

p=&gr1;

// указатель на объект базового класса

 

 

p->see();

// вызов функции базового класса объекта gr1

 

 

 

 

 

 

 

Г

 

 

(&gr2)->see(); // вызов функции базового класса объекта gr2

 

 

 

 

 

 

 

Б

 

 

p=&st;

// указатель на объект производного классаУ

 

p->see();

// вызов функции производного класса объекта st

 

return 0;

 

 

 

 

а

 

 

}

 

 

 

 

 

 

 

 

 

к

 

 

 

Результат работы программы:

 

 

 

 

 

 

 

 

 

 

факультет 1

123456

 

 

 

 

 

 

 

факультет 2

345678

кт

 

 

 

 

 

факультет 2",150502

 

е

 

 

 

 

Иванов 4 5 5 3

 

 

 

 

 

Применение указателяона бъе базового класса позволяет использовать

его для всех производных класс в. Объявление virtual void see(void) говорит о том, что функция see можетибыть различной для базового и производных клас-

сов. Тип виртуальнойлфункц не может быть переопределен в производных классах. Исключением яв яется случай, когда возвращаемый тип виртуальной функции являетсябуказате ем или ссылкой на порожденный класс, а виртуаль- ная функцияиросновного класса − указателем или ссылкой на базовый класс. В производных классах функция может иметь список параметров, отличный от параметровБв туальной функции базового класса. В этом случае эта функция будет не в туальной, а перегруженной. При этом спецификатор virtual игнори- руется. Вызов функций должен производиться с учетом списка параметров.

Если функция вызывается с использованием ее полного имени grup::see(), то виртуальный механизм игнорируется. Игнорирование этого может привести к серьезной ошибке:

void stud::see(void) { see();

. . . . }

В этом случае инструкция see() приводит к бесконечному рекурсивному вызову

76

функции see().

Рассмотрим еще один пример программы, в которой использованы вирту-

альные функции.

 

 

 

 

 

 

 

 

 

#include <iostream>

 

 

 

 

 

 

 

using namespace std;

 

 

 

 

 

 

 

class vehicle

// класс «транспортное средство»

 

 

{

int _vehicle;

 

 

 

 

 

 

 

 

public:

 

 

 

 

 

 

 

 

 

 

Р

 

 

 

// описание виртуальной функции message класса vehicle

 

virtual void message(void) {cout << "Транспортное средство\n";}

};

 

 

 

 

 

 

 

 

 

 

И

 

 

 

 

 

 

 

 

 

 

 

 

class car : public vehicle // класс «легковая машина»

 

 

{

int _car;

 

 

 

 

 

 

 

 

 

public:

 

 

 

 

 

 

 

 

Г

 

 

 

 

 

// описание виртуальной функции message класса car

};

void message(void) {cout << "Легковая машинаУ\n";}

 

 

 

 

 

 

 

 

асс

 

 

class truck : public vehicle // кл

 

 

 

«грузовая машина»

 

{

int _trunk;

 

 

к

Б

 

 

public:

 

 

 

 

 

 

 

 

 

 

 

 

е

 

 

 

 

};

int fun(void) {return trunk;}

 

 

 

 

 

 

 

 

т

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

class boat : public vehicle // класс «лодка»

 

 

{

int _boart;

 

 

 

 

 

 

 

 

public:

 

 

о

 

 

 

 

 

 

 

 

 

 

опи

 

 

 

 

 

 

 

 

 

int func(void) {return boart;}

 

 

 

 

 

 

//

сан е виртуальной функции message класса boat

 

void message(void) {cout << "Лодка\n";}

 

 

б

 

 

 

 

 

 

 

 

 

 

};

 

 

 

 

 

 

 

 

 

 

 

 

voidлmain()

 

 

 

 

 

 

 

 

 

{

vehicle *p;

 

// описываем p как указатель на

 

иp = new vehicle;

// объект класса vehicle

 

 

// создаем объект класса vehicle,

 

Б p->message();

 

// указатель p указывает на этот объект

 

// вызываем метод message объекта vehicle

 

delete p;

 

 

// удаляем объект p

 

 

p = new car; p->message(); delete p;

p = new truck;

77

p->message();

// p->fun(); error fun is not a member of ’ vehicle’ delete p;

p = new boat; p->message(); delete p;

}

 

Результат работы программы:

Р

Транспортное средство

 

Легковая машина

И

Транспортное средство

 

Лодка

 

Классы car, truck и boat являются производными от базового класса

vehicle. В базовом классе vehicle описана виртуальная функция message. В двух

 

 

 

 

Г

из трех классов(car, boat) также описаны свои функции message, а в классе truck

нет описания своей функции message.

 

 

Б

У

Если в базовом классе у функции message отсутствует спецификатор

virtual, то компилятор связал бы любой вызов мето

 

объекта указателя p с ме-

 

 

да

 

 

тодом message класса vehicle, так как при его описании указано, что переменная

 

к

 

 

 

p указывает на объект класса vehicle, т.е. было бы произведено раннее связыва-

 

е

 

бы вывод четырех строк

ние. Результатом работы такой программы был

«Транспортное средство».

 

 

 

 

 

При работе с объектами классов car и boat вызываются их собственные

методы message, что и подтверждае ся выводом на экран соответствующих со- общений. У класса truck нет своего ме ода message, по этой причине произво-

дится вызов соответствующ

ме

да базового класса vehicle.

Заметим, что деструкт р м ж

быть виртуальным, а конструктор − нет.

 

ет

Замечание. В чем разн ца между виртуальными функциями (методами) и

его

 

переопределением функц ?

 

 

Что бы измени осьли, ес

бы функция see() не была бы описана как вирту-

альная? В этомбс учаелрешение о том, какая именно из функций see() должна быть выполнена, удет принято при ее компиляции.

разомБ. Принисоздан и нового объекта для него выделяется память. Для вирту- альных функций (и только для них) создаются виртуальные таблицы (virtual table, сокращенно vtbl) и указатель на виртуальную таблицу (virtual table pointer, сокращенно vptr). Доступ к виртуальной функции осуществляется через этот указатель и соответствующую таблицу (т.е. выполняется косвенный вызов функции). Виртуальная таблица обычно представляет собой массив указателей на функции. Каждый класс, в котором объявляются или наследуются виртуаль- ные функции, имеет свою виртуальную таблицу. Например:

Меха зм вызова виртуальных функций можно пояснить следующим об-

class A

78

{public:

A();

virtual ~A(); virtual f1(); virtual char f2();

. . .

};

Виртуальная таблица будет иметь примерно такой вид:

 

хххх

реализация A::A

Р

 

 

vtbl A

хххх

реализация A::f1

 

 

 

И

 

хххх

реализация A::f2

 

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

большое число виртуальных функций, то соответствующие им таблицы могут

занимать достаточно много места.

У

Далее возникает вопрос, где хранятся эти таблицы. Один из подходов за-

 

Г

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

ектный файл. Затем компоновщик уд ляет Бдубликаты, оставляя одну таблицу в конечном исполняемом файле или библиотеке. Другой, более распространен-

ный подход, состоит в том, что виртуальн я таблица создается в объектном файле первой не inline виртуальной фун ции. Для класса А это объектный файл

A::~A(). Если все виртуальныеефункции объявлены как inline, то копии вирту- альной таблицы будут созданы во всех объектных файлах, использующих ее.

В то же время vtbl, являясь частью механизма виртуальных функций, являет-

ся бесполезной без указа елей vprt. Они устанавливают соответствие между объек-

том и некоторой vtbl. тКаждый объект, класс которого объявляет виртуальную

функцию, содерж т vprt, добавляемый компилятором к объекту (рис. 2).

 

 

 

о

 

 

 

 

 

и

 

 

 

 

 

 

данные

 

 

л

 

 

vprt

 

 

 

Рис. 2.

Схема виртуального объекта

 

бСвойство виртуальности проявляется только тогда, когда обращение к

функц

дет через указатель или ссылку на объект. Указатель или ссылка мо-

и

 

 

 

 

 

Б

 

 

 

 

 

 

гут указывать как на объект базового, так и на объект производного классов. Если в программе имеется сам объект, то уже на стадии компиляции известен его тип и, следовательно, механизм виртуальности не используется. Например:

func(cls obj)

{

obj.vvod(); // вызов компоненты-функции obj::vvod

}

func1(cls &obj)

79

{

 

 

 

 

obj.vvod();

// вызов компоненты-функции в соответствии

 

}

 

// с типом объекта, на который ссылается obj

 

Виртуальные функции позволяют принимать решение в процессе выпол-

нения.

 

 

 

 

#include

<iostream>

 

 

#include

<iomanip>

 

 

using namespace std;

 

 

#include

<string.h>

 

Р

#define N 5

 

 

 

И

class base

// базовый класс

 

 

{ public:

virtual char *name(){ return " noname ";} У

virtual double area(){ return 0;}

}; Г class rect : public base // производный класс «прямоугольник»

{int h,s;

public:

 

 

 

 

 

 

Б

 

 

 

 

 

 

 

 

virtual char *name(){ return " прямоугольни ";}

 

rect(int H,int S): h(H),s(S) {}

 

к

 

 

 

 

ка

};

double area(){ return h*s;}

 

 

 

 

 

т

 

 

 

 

 

 

 

 

 

 

class circl : public base // производный класс «окружность»

{ int r;

 

о

е

 

 

public:

 

 

 

 

 

и

 

 

 

 

 

 

virtual char *name(){ return " круга ";}

 

 

 

circl(int R): r(R){}

 

 

 

 

 

 

 

 

л

 

 

 

 

 

 

};

double area(){ return 3.14*r*r;}

 

 

 

 

 

 

 

 

 

 

 

int main()

 

 

 

 

 

 

 

 

и

 

 

 

 

 

 

 

{ base *p[N],a;

 

 

 

 

 

 

Б

 

 

 

 

 

 

 

double sбarea=0;

 

 

 

 

 

 

rect b(2,3); circl c(4);

for(int i=0;i<N;i++) p[i]=&a; p[0]=&b;

p[1]=&c;

for(i=0;i<N;i++)

cout << "плошадь" << p[i]->name() << p[i]->area() << endl; return 0;

}

80

Массив указателей p хранит адреса объектов базового и производных классов и необходим для вызова виртуальных функций этих классов. Виртуаль- ная функция базового класса необходима тогда, когда она явно вызывается для базового класса или не определена (не переопределена) для некоторого произ- водного класса.

Если функция была объявлена как виртуальная в некотором классе (базо- вом классе), то она остается виртуальной независимо от количества уровней в иерархии классов, через которые она прошла.

class A

{. . .

public: virtual void fun() {}

};

Р

И

class B : public A

{ . . .

У

public: void fun() {}

};

БГ

class C : public B

{ . . .

 

 

 

 

public:

 

 

 

 

 

а

 

 

 

. . .

// в объявлении л сса С отсутствует описание функции fun()

 

};

 

 

 

 

 

е

 

 

main()

 

 

т

к

 

{ A a,*p=&a;

 

 

 

 

 

 

 

 

 

B b;

 

о

 

 

 

 

 

C c;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

p->fun();

 

// выз в версии виртуальной функции fun для класса А

 

 

p=&b;

 

 

 

 

 

 

 

 

 

л

// вызов версии виртуальной функции fun для класса B

 

 

p->fun();

 

 

б

 

 

 

 

 

 

 

 

p=&c;и

 

 

 

 

 

 

p->fun();

 

// вызов версии виртуальной функции fun для класса B (из А)

и

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

Б

Если в производном классе виртуальная функция не переопределяется, то

спользуется ее версия из базового класса.

class A

{ . . .

public: virtual void fun() {}

};

class B : public A

{ . . .

public: void fun() {}

};

81

class C : public B

{. . .

public: void fun() {}

};

 

 

 

 

 

 

 

 

 

 

int main()

 

 

 

 

 

 

 

 

 

{ A a,*p=&a;

 

 

 

 

 

 

 

 

 

 

B b;

 

 

 

 

 

 

 

 

 

 

C c;

 

 

 

 

 

 

 

 

 

 

p->fun();

// вызов версии виртуальной функции fun для класса А

 

p=&b;

 

 

 

 

 

 

 

 

 

 

p->fun();

// вызов версии виртуальной функции fun для класса B

 

p=&c;

// вызов версии виртуальной функции fun для класса СР

}

p->fun();

Основное достоинство данной иерархии состоит в том, что кодИне нужда-

ется в глобальном

изменении, даже при добавлении вычислений площадей но-

 

 

 

 

 

 

 

 

 

У

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

ру кода распространяются автоматически.

 

Г

Рассмотрим механизм вызова виртуальной функции базового и производ-

ного классов из компонент-функций этих

л ссов, вызываемыхБ

через указатель

на базовый класс.

 

 

 

 

 

е

а

 

 

 

 

 

 

 

 

class A

 

 

 

 

 

 

 

 

 

 

 

 

 

к

 

{ public :

 

 

 

 

т

 

 

virtual void f()

 

 

 

 

 

 

 

 

 

 

 

 

{ return; }

 

 

о

 

 

 

 

 

void fn()

 

 

 

 

 

 

 

 

 

 

и

 

 

 

 

 

 

{ f(); }

 

 

 

 

 

 

 

};

// выз в функции f

 

 

 

 

л

 

 

 

 

 

 

 

 

 

 

 

 

 

 

class B : public A

 

 

 

 

 

 

 

{ public:

 

 

 

 

 

 

 

 

 

 

void f()

 

 

 

 

 

 

 

 

 

 

и

}

 

 

 

 

 

 

 

 

 

{ return;

 

 

 

 

 

 

 

 

Б

 

 

 

 

 

 

 

 

 

 

void fn()б

 

 

 

 

 

 

 

 

};

{ f(); }

 

// вызов функции f

 

 

 

 

 

 

 

 

 

 

 

 

 

int main()

{A a,*pa=&a; B b,*pb=&b;

pa->fn(); // вызов виртуальной функции f класса А через A::fn() pa = &b;

pa->fn(); // вызов виртуальной функции f класса В через A::fn()

82

pb->fn(); // вызов виртуальной функции f класса В через B::fn()

}

В инструкции pa->fn() выполняется вызов функции fn() базового класса А, так как указатель pa указатель на базовый класс и компилятор выполняет вызов функции базового класса. Далее из функции fn() выполняется вызов вна- чале виртуальной функции f() класса А, так как указатель pa инициализирован адресом объекта класса А. Затем, после инициализации pa адресом объекта класса В выполняется вызов виртуальной функции f() класса В из функции fn() класса А. Далее, используя указатель pb, инициализированный также адресом объекта класса В, вызывается функция f() класса В через функцию fn() класса В.

 

В заключение рассмотрим механизм вызова виртуальных функций внутри

конструкторов и деструкторов.

 

 

 

 

 

Р

 

#include <iostream>

 

 

 

 

 

 

 

 

 

 

 

 

И

 

using namespace std;

 

 

 

 

 

 

class Base {

 

 

 

 

 

 

 

 

 

 

 

 

 

 

У

 

 

public:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Г

 

 

 

 

Base()

 

 

 

 

 

 

 

 

 

 

{ cout << " конструктор базового класса"<<endl;

 

 

 

 

f1();

 

 

 

 

 

 

 

Б

 

 

 

 

 

}

 

 

 

 

 

 

а

 

 

 

 

 

~Base()

 

 

 

 

 

 

 

 

 

 

{ cout << " деструктор базового

ласса"<<endl;

 

 

 

 

f2();

 

 

 

 

 

к

 

 

 

 

 

 

 

 

 

 

е

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

virtual void f1() { cout << " функция f1 базового класса"<<endl; }

 

 

virtual void f2() { cout << " функция f2 базового класса "<<endl; }

 

 

 

 

 

 

т

 

 

 

 

 

 

 

 

void f3(){ cout<<" функция f3 базового класса "<<endl;

 

 

 

 

 

 

оf1();

 

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

};

 

и

 

 

 

 

 

 

 

 

 

л

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

б

 

 

 

 

 

 

 

 

 

 

 

 

class Derived : public Base

 

 

 

 

 

и{ public:

 

 

 

 

 

 

 

 

 

 

Б

 

Derived()

 

 

 

 

 

 

 

 

 

 

 

{ cout<<"конструктор производного класса "<<endl; }

 

~Derived()

{ cout<<"деструктор производного класса "<<endl; }

virtual void f1() { cout << " функция f1 производного класса"<<endl; } virtual void f2() { cout << " функция f2 производного класса"<<endl; }

};

83

int main()

{cout << "создание обекта"<<endl; Derived *pd = new Derived();

cout << "разрушение объекта"<<endl; delete pd;

}

 

 

 

 

Результат работы программы:

 

 

 

 

конструктор базового класса

 

 

 

Р

функция f1 базового класса

 

 

 

 

 

 

 

конструктор производного класса

 

 

И

функция f3 базового класса

 

 

 

 

 

 

функция f1 производного класса

 

У

 

деструктор производного класса

 

 

 

 

 

 

деструктор базового класса

Г

 

 

функция f2 базового класса

 

 

 

 

 

 

При вызове виртуальных функций в конструкторе или деструкторе ком-

 

Б

 

 

 

пилятор вызывает локальную версию функции, т.е. механизм виртуальных вы-

зовов в конструкторах и деструкторах не работает. Это происходит потому, что

 

 

 

 

 

 

 

ила

vtbl во время вызова конструктора полностью не инициализирована.

Аналогично в деструкторе вызывается фун ция б зового класса, потому как

объект производного класса был уже уничтожен (был вызван его деструктор).

Перечислим основные свойства и пр в

использования виртуальных

функций:

 

 

 

 

т

к

 

- виртуальный механизм подд ржива т полиморфизм на этапе выполне-

ния программы. Это значит, ч

 

ребуемаяверсия программы выбирается на

этапе выполнения программы, а не компиляции;

 

- класс, содержащ

х тя бы

дну виртуальную функцию, называется по-

лиморфным;

 

л

о

 

 

 

- виртуальные функц

можно объявить только в классах (class) и струк-

турах (struct);

б

ий

 

 

 

 

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

и

 

 

 

 

 

 

 

(без специф катора static), так как характеристика virtual унаследуется. Функция

порожденного класса автоматически становится virtual;

Б

 

 

 

 

 

 

 

 

- в ртуальные функции можно объявить со спецификатором friend для другого класса;

- виртуальными функциями могут быть только неглобальные функции (т.е. компоненты класса);

- если виртуальная функция, объявленная в базовом классе со специфика- тором virtual, переопределена в производном без спецификатора virtual, то она при этом остается виртуальной независимо от уровня наследования. То есть механизм виртуализации функций наследуется;

- для вызова виртуальной функции требуется больше времени, чем для

84

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]