Программирование >>  Унарные и бинарные операторы 

1 ... 25 26 27 [ 28 ] 29 30 31 ... 37


Вспомним, что при увеличении па единицу ука.эатсль нерсдвша-ется к cnedywiiie.uy объекту. Следовательно, указателю известен размер объекта, на который он указывает.

Но где хранить такую смесь? Первым приходит па ум обычгсый массив, по ведь там хранятся одинаковые объекты, а наши классы point и cpoint хоть и похожи, но неодинаковы по размеру. Причем строка col, добавленная в класс cpoint, может, в зависимости от цвета, занимать разное число символов.

Можно, конечно, выделить каждому объекту стшп.ко памяти, чтобы он обязательно поместился в массив, ио такое решение некрасиво, потому irro требует лишнюю память. Гораздо изящней поместить в массив не слии объекты, а указатели на них. Ведь указатель - .это адрес начала объекта, а размер атреса не зависит от размера объекта, он одинаков как для обычных (poi nt), так и для цветных (cpoint) точек.

Остается рептть, массив каких указателей должен быть создан. Очевидно, это пе могут быть указатели па производные классы, ведь указатель на объект - не просто адрес, это еще и anainie всех составных частей объекта, а значит, и его размера*. Если р - указатель па объект класса point, то р->х - это горизонтальная координата точки, а р->у - вертикальная координата. Еслп же в указателе на объект производного класса хрангггь адрес объекта базового класса, у программиста появится доступ к несуществующим данным (в нашем примере-к цвету точки), что очень опасно.

Но сдругой стороны, не очень понятно, как использовать указатель на объекг ба.зового класса. Ведь ему неизвестны данные и собственные фун кции класса производного. Программа, иллюстрирующая сказанное (листинг 10.6), содержит те же определения классов point и cpoint, что и программа из листинга 10.5.

Листинг 10.6

#include <lostream> #include <string> using namespace std: class point{

class cpoint:public point {

}:

int main(){ point p(2.3); cpoint cp(4.5. red ): point *pp: pp=&p;

pp->show(): (2.3) pp=&cp:

pp->5how(): (4.5)

В этой профамме создаются объект ы базового и производного классов point н cpoint. Далее указателю на базовый класс присваивается сначала адрес объекта базового класса р (рр=&р), а затем адрес объекта производного класса (рр=&ср). И хоть указатель правильно ориентируется в адресах объектов, он остается указателем базового класса и используется функцией showO, известггой только базовому классу. Значение цвета он не выводит.

Как же сделать так, чтобы по ад]ссу объекта чудесным образом определялся его тип и вызывалась нужная функция? Оказывается, в С++ для этого достаточно использовать одно лишь слово virtual, поставленное перед именем функции базового класса. В листинге 10.7 показана профамма, которая вызывает функцию show() в соответствии с типом объекта. Ecjhi в указателе хранится адрес объекта базового класса, вызовется функция show(), выводящая на экран только координаты точ ки. Если же указатель содержит адрес объекта произвол-



ру, какую функцию вызывать, применяется оператор распшрения области видимости - point: :show().

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

1. Создать в базовом и производных классах одноименные функции (в нашем примере это show()), которые и займутся обработкой.

2. Перед объявлением функции в базовом классе поставить ключевое слово virtual.

3. Создать массив указателей на объекты базового класса и поместить туда указатели на наши объекты.

Пример программы, обрабатываюи1ей объекты базового и производных классов, показан в листинге 10.8.

Листинг 10.8

#include <iostream> #include <string> using namespace std: class point{

virtual void show(){} }:

class cpoint:public point {

void show(){}

class ccpoint:public cpoint { public:

ccpoint(double xc. double yc. string s.int w): cpoint (xc.yc.s).width(w){}: void show(){

cpoint: :show(): cout < = width:

продолжение

ного класса, вызовется функция showO, выводящая на экран не только координаты, но и цвет точки.

Листинг 10.7

#include <iostream> #include <string> using namespace std: class point{ public;

point(double xc. double yc): x(xc).y(yc){}

virtual void show(){

cout ( X . у ) ;

protected: double x: double y:

class cpoint:public point { public:

cpomt (double xc. double yc. string s):

point(xc.yc).col(s){}

void show(){

point::show():

cout col:

protected: string col:

int main(){ point p(2.3): cpoint cp(4.5. red ): point *pp: pp=&p:

pp->show(): cout endl: (2.3) pp=&cp:

pp->show(): cout endl: II (4.5) red

return 0:

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



Идеи и вещи

Идеи (высшая из них - идея блага) - вечные и неизменные умопостигаемые прообразы ВСН1СЙ, всего преходящего и изменчивого бытия; Bcnui - подобия и отражения идей.

Большой энциклопедический словарь, статья Платон -

Согласью фьиюсофии Платона предметы нашего мира - суть иесоверн1Снные отражения вдей из иного, идеального мира. Существует идея яблока, собаки, кошки, велосипеда и т. д. Не правдали, классы языка C+-I- и созданные

Листинг 10.8 {продолжение protected: int width; }:

int main(){

point *ppts[3]: массив указателей ppts[D]=new point(2.3): ppts[l]=new cpoint(4.5. red ); ppts[2]=new ccpo1nt(6.7. blue .2); for(int i-0: i<3: i++){

ppts[i]->show():

cout endl;

return 0: }

В .этой программе кроме классов point и cpoint, определенных в листинге 10.7, есть enie один класс - ccpoi nt, прои.зводпый от cpoint. В объектах класса ccpoint добавлен еще один признак точки - ширина width. Новый класс порожден классом cpoint, то есть классу point он приходится как бы внуком. И несмотря на это, одного слова virtual в самом старшем классе point достаточно, чтобы программа по указателю на объект точно определила, какую функцию show() вызывать. .

Действительно, в функции mainO (см. листинг 10.8) создается массив из трех указателей на объект класса point - *ppts[3]. Затем создаются объекты point, cpoint и ccpoint, причем указатели на них засылаются в массив. Так, указатель на ccpoint засылается инструкцией:

ppts[2]=new ccpoi nt(б.7. blue .2);

Далее указатели перебираются в цикле, и для каждого объекта, на который они указывают, вызывается функция showO. Как будет выполняться инструкция ppts[i]->show(),зависит от того, есть ли слово virtual перед объявлением функции showC) в базовом классе. Еслп оно есть, вызывается функция show(), соответствующая объекту, на который направлен указатель, если нет -

функция show() базового класса. В nanicM примере на нулевом обороте цикла (1=0) функция showO класса point покажет на экране (2.3), на первом обороте та же функция, но определенная в классе cpoint, покажет (4.5) red и, наконец, на втором, последнем обороте будет вызвана функция showO класса ccpoint, которая выведет на экран (6.7) blue 2.

Задача 10.2. Почему меняется размер объек гов классов point и cpoint при определении функции show() базового класса point как virtual?

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



1 ... 25 26 27 [ 28 ] 29 30 31 ... 37

© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика