Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

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

Поля типа

Для того, чтобы через указатель на базовый класс вызывать подходящие функции (programmer2::print или employee::print), мы должны решить, какому типу на самом деле принадлежит объект, на который указывает указатель?

Для этого мы можем ввести в базовый класс поле типа, чтобы заинтересованные функции могли его проверить:

 

class employee{

public:

enum Rab_type{R,P};

Rab_type type;

employee():type(R){}

void print()const;

};

class programmer:public employee{

public:

programmer(){type=P;}

void print()const;

};

 

void employee::print()const{

cout<<"employee::print"<<endl;

}

void programmer::print()const{

cout<<"programmer::print"<<endl;

}

 

void print(employee* pr){

switch(pr->type){

case employee::R:

pr->print();

break;

case employee::P:

const programmer* p=static_cast<const programmer*>(pr);

p->print();

break;

}

}

Добавление нового производного от employee класса подразумевает внесение изменений во все ключевые функции системы:

1) в базовом классе employee в перечислимый тип добавляется новая константа;

2) пишется производный класс (аналогично programmer);

3) в функцию ::print добавляется новая ветвь.

 

 

Виртуальные функции решают проблему, связанную с полем типа. Для того, чтобы сделать функцию виртуальной, нужно поставить ключевое слово virtual слева от объявления функции в классе. Например:

 

class employee_v{

public:

virtual void print()const;

};

 

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

void employee_v::print()const{

cout<<"employee_v::print"<<endl;

}

 

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

class programmer_v:public employee_v{

public:

virtual void print()const;

};

 

void programmer_v::print()const{

cout<<"programmer_v::print"<<endl;

}

Вызов виртуальной функции синтаксически такой же, как и обычной функции:

 

employee_v r1;

programmer_v p1;

r1.print();//employee_v::print

p1.print();//programmer_v::print

В этих двух случаях вызовутся функции print соответствующих классов. Так было бы и в случае невиртуальных функций print (т.е. без ключевого слова virtual).

 

void print_v(employee_v* pr){

pr->print();//employee_v::print или programmer_v::print

}

В этом случае мы вызываем функцию-член (print) по указателю pr на employee_v. В случае, если бы функция-член print была объявлена невиртуальной, то однозначно вызвалась бы функция employee_v::print (что определяется типом указателя). Однако, так как функция print виртуальна, вызывается либо employee_v::print, либо programmer_v::print, в зависимости от того, указывает pr на объект типа employee_v либо на объект типа programmer_v. Например:

employee_v r1,r2;

programmer_v p1,p2;

print_v(&r1);// employee_v::print

print_v(&p1);// programmer_v::print

Теперь мы можем запомнить указатели на различные объекты в массиве и вызвать print_v в цикле:

employee_v *record[4]={&r1,&p1,&r2,&p2};

for(int i=0;i<4;++i){

print_v(record[i]);

}

Вызовутся функции в таком порядке: employee_v::print, programmer_v::print, employee_v::print, programmer_v::print.

Так как наша функция print_v выродилась в единственный вызов функции-члена print, мы можем написать непосредственно (что эквивалентно предыдущему примеру):

for(int i=0;i<4;++i){

record[i]->print();

}

В случае, если мы используем виртуальные функции, добавление нового производного от employee класса не требует изменений ни в базовом классе, ни в коде, использующем виртуальные функции (в нашем случае этот код – приведенный выше цикл). Заметьте, что это будет работать, даже если указанный цикл был написан и откомпилирован до того, как производный класс programmer_v::print был вообще задуман! Данный факт служит краеугольным камнем объектно-ориентированных проектов и придает стабильность развивающейся программе.

Полиморфизм

Когда функции базового класса (employee_v) ведут себя «правильно» независимо от того, какой конкретно производный класс используется, это называется полиморфизмом. Тип, имеющий виртуальные функции, называется полиморфным типом. Для достижения полиморфного поведения в C++ вызываемые функции-члены должны быть виртуальными, и доступ к объекту должен осуществляться через ссылки или указатели. При непосредственных манипуляциях с объектом (без помощи указателя или ссылки) его точный тип известен компилятору, и поэтому полиморфизм времени выполнения не требуется.

 

 

Абстрактные классы

 

Многие классы схожи с классом employee_v в том, что они полезны как сами по себе (employee_v хранит информацию об имени и ставке работника и может распечатать эти данные), так и в качестве базы для производных классов (programmer_v и других). Для таких классов методы, описанные в предыдущем разделе (виртуальные функции), являются вполне достаточными. Однако не все классы соответствуют такому образцу. Некоторые классы, такие как figure, представляют собой абстрактную концепцию, для которой не могут существовать объекты. Класс figure имеет смысл только в качестве базы для производных классов.

 

class figure{

public:

void move_to(int x, int y){_x=x; _y=y;}

virtual void draw(HDC);

virtual void rotate(double);//angle measured in degrees

protected:

figure(int x,int y):_x(x),_y(y){}

int _x,_y;

};

 

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

 

class circle:public figure{

public:

circle(int x,int y,int r)

:figure(x,y),_r(r){}

virtual void draw(HDC);

virtual void rotate(double){};

private:

int _r;

};

 

class line:public figure{

public:

line(int x0, int y0,

int x1, int y1)

:figure(x0,y0),_dx(x1-x0),_dy(y1-y0)

{}

virtual void draw(HDC);

void rotate(double);

private:

int _dx,_dy;

};

 

Для figure невозможно разумно определить виртуальные функции:

 

void figure::draw(HDC){

assert(false);//этот код не должен быть вызван

}

void figure::rotate(double){

assert(false);//этот код не должен быть вызван

}

 

Попытка создания фигуры допустима, но неразумна:

figure f1(200,200);//фигура с центром, но без формы

circle c1(100,100,10);

line l1(120,100,130,105);

figure* mas[2]={&c1,&l1};

for(int i=0;i<2;++i){

mas[i]->draw(hdc);

 

mas[i]->move_to(100,110);

mas[i]->draw(hdc);

 

mas[i]->move_to(100,140);

mas[i]->rotate(40);

mas[i]->draw(hdc); }

 

<== предыдущая лекция | следующая лекция ==>
Преобразование типов. UML расшифровывается как Unified Modeling Language – унифицированный язык моделирования | Исключение ненужных инстанцирований функций-членов
Поделиться с друзьями:


Дата добавления: 2014-01-11; Просмотров: 286; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.021 сек.