Использование наследования

В языке C++ наследование устанавливается только при составлении программы и не может быть изменено в процессе её выполнения. Поведение каждого объекта полностью определяется классом этого объекта и одинаково для всех объектов данного класса. Все характеристики наследования одинаковы для всех объектов каждого класса.

Список суперклассов (если он не пуст) указывается в начале определения каждого класса; подкласс называется также производным классом. В следующем примере приведено описание класса Item, а также описания подкласса Shape класса Item и подклассов Box и Circle класса Shape:

class Item

{

public:

virtual void cut ();

virtual void move (Length deltax, Length deltay) = 0;

virtual Boolean pick (Length px, Length py) = 0;

virtual void ungroup () = 0;

};

class Shape: public Item

{

protected:

Length x;

Length y;

public:

void cut ();

void draw () {write (COLOR_FOREGROUND);};

void erase (); {write (COLOR_BACKGROUND);};

void move (Length deltax, Length deltay);

Boolean pick (Length px, Length py) = 0;

void ungroup () { };

virtual void write (Color color) = 0;

};

class Box: public Shape

{

protected:

Length width;

Length height;

public:

Box (Length x0, Length y0, Length width0, Length height0);

Boolean pick (Length px, Length py);

void write (Color color);

};

class Circle: public Shape

{

protected:

Length radius;

public:

Circle (Length x0, Length y0, Length radius0);

Boolean pick (Length px, Length py);

void write (Color color);

};

Члены суперкласса (атрибуты и операции) наследуются его подклассами (члены, определённые в суперклассе, имеются у всех его подклассов). Члены суперкласса, определённые в нём как private, недоступны для операций его подклассов; для операций подклассов доступны члены суперкласса, определённые в нем protected и как public. Методы, определённые в суперклассе, могут быть переопределены в (некоторых) его подклассах, если они определены как виртуальные (virtual). Например, метод write класса Shape может быть переопределён в его подклассах Box и Circle, поэтому он определён как виртуальный; методы и в подклассах не переопределяются, поэтому их можно не объявлять как виртуальные. Если в определении виртуального метода указана его «инициализация» к 0 (virtual void write (Color color) = 0;), то он обязательно должен быть переопределён в каждом его подклассе (такой метод называется абстрактным). Класс, содержащий хотя бы один абстрактный метод, также называется абстрактным. Для абстрактных классов нельзя порождать объекты (объекты определены только для его подклассов). Если все методы класса определены как абстрактные, то говорят, что такой (абстрактный) класс определяет интерфейс, реализуемый в его подклассах.

В языке C++ поддерживается множественное наследование: каждый класс может иметь один или несколько суперклассов.

Реализация зависимостей

Зависимости в языке C++ реализуются с помощью указателей или с помощью специальных объектов. Например, зависимость «много-к-одно-му» между классами Item и Group реализована через указатели:

class Item{

public:

virtual void cut ();

virtual void move (Length deltax, Length deltay) = 0;

virtual Boolean pick (Length px, Length py) = 0;

virtual void ungroup () = 0;

private:

Group* group;

friend Group::add_item (Item*);

friend Group::remove_item (Item*);

public:

Group* get_group () {return group;};

};

class Group: public Item

{

public:

void cut ();

void move (Length deltax, Length deltay);

Boolean pick (Length px, Length py) = 0;

void ungroup () { };

private:

ItemSet* items;

public:

void add_item (Item*);

void remove_item (Item*);

ItemSet* get_items () {return items;}

};

Каждый раз, когда к зависимости добавляется (или из неё удаляется) связь, оба указателя должны быть изменены:

void Group::add_item (Item* item)

{

item->group = this;

items->add (item);

}

void Group::remove_item (Item* item);

{

item->group = 0;

items->remove (item);

}

Методы Group::add_item и Group::remove_item могут изменять приватные (private) атрибуты класса Item, хотя они определены в его подклассе Group, так как они определены как дружественные (friends) для класса Item.

В рассмотренном примере опущены проверки:

- не является ли включаемый в группу графический объект уже членом этой группы: в этом случае его незачем ещё раз включать в группу;

- не является ли включаемый в группу графический объект членом какой-либо другой группы: в этом случае его нельзя включать в группу и необходимо выдать на экран соответствующее сообщение.

Иногда связанные между собой (зависимые) объекты включают в так называемые коллективные классы. В качестве примера такого класса рассмотрим класс ItemSet (набор объектов):

class ItemSet

{

public:

ItemSet(); //создать пустой набор объектов

~ItemSet(); //уничтожить набор объектов

void add(Item*); //включить объект в набор

void remove(Item*); //исключить объект из набора

Boolean includes(Item*); //проверить наличие объекта в наборе

int size(Item*); //определить количество объектов в наборе

};

Коллективные классы часто используются в библиотеках классов. При работе с коллективными классами удобно использовать итераторы, т.е. объекты, с помощью которых можно «просмотреть» коллектив.

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

 

5.3.6. Шаблоны в языке C++

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

Параметрическое программирование в языке C++ реализовано с помощью шаблонов (template). В C++ определено два вида шаблонов: шаблоны-классы и шаблоны-функции.

Шаблоны-классы могут использоваться многими способами, но наиболее очевидным является их использование в качестве адаптивных объектов памяти. Шаблоны-функции могут использоваться для определения параметризованных алгоритмов. Основное отличие шаблона-функции от шаблона-класса в том, что не нужно сообщать компилятору, к каким типам параметров применяется функция, он сам может определить это по типам её формальных параметров.

Возможность параметрического программирования на языке C++ обеспечивается стандартной библиотекой шаблонов STL (Standard Template Library). Она включена в последнюю предварительную версию Стандарта языка C++. Библиотека подробно описана в книге D.R.Musser, A.Saini «STL Tutorial and Reference Guide. C++ Programming with the Standard Template Library» Addison-Wesley, 1996 (книга доступна также по http://www.aw.com/cp/musser-saini.html). Документация по текущей версии STL доступна по http://weber.u.washington.edu/~bytewave/bytewave_stl.html или http://www.ualberta.ca/~nyu/stl/stl.html. Наконец, реализация текущей версии STL доступна по ftp://ftp.cs.rpi.edu/pub/stl.