Механизм позднего связывания

Для каждого класса (не объекта!), содержащего хотя бы один виртуальный метод, компилятор создает таблицу виртуальных методов (vtbl), в которой для каждого виртуального метода записан его адрес в памяти. Адреса методов содержатся в таблице в порядке их описания в классах. Адрес любого виртуального метода имеет в vtbl одно и то же смещение для каждого класса в пределах иерархии.

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

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

Рекомендуется делать виртуальными деструкторы для того, чтобы гарантировать правильное освобождение памяти из-под динамического объекта, поскольку в этом случае в любой момент времени будет выбран деструктор, соответствующий фактическому типу объекта. Деструктор передает операции delete размер объекта, имеющий тип size_t. Если удаляемый объект является производным и в нем не определен виртуальный деструктор, передаваемый размер объекта может оказаться неправильным.

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

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

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


 


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

Класс, содержащий хотя бы один чисто виртуальный метод, называется абстрактным. Абстрактные классы предназначены для представления общих понятий, которые предполагается конкретизировать в производных классах. Эти классы применяются при определении данных и методов, которые будут общими для различных производных классов. Создание классов, имеющих общим только поведение, осуществляется с помощью базового класса, в котором все функции-члены объявлены в разделе public, а в разделе private данных либо очень мало, либо вовсе нет. Абстрактный класс может использоваться только в качестве базового для других классов — объекты абстрактного класса создавать нельзя, поскольку прямой или косвенный вызов чисто виртуального метода приводит к ошибке при выполнении.

Функции-члены в абстрактных классах определяются через чистые виртуальные функции. Чистые виртуальные функции – функции, которые объявлены с ключом virtual, но не определяются. Они должны быть переопределены в производных классах. В базовом классе они инициализируются с помощью идентификатора (=0). Форма записи чистой виртуальной функции:

virtual <тип возвращаемого значения> <имя функции> (<список параметров>) = 0;

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

При определении абстрактного класса необходимо иметь в виду следующее:

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

· допускается объявлять указатели и ссылки на абстрактный класс, если при инициализации не требуется создавать временный объект;

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

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

Пример 1

Class DIsplay_medium

{

public:

virtual Point size() const = 0;

virtual Point cursor() const = 0;

virtual int move_cursor(const Point &p) = 0;

virtual Display char character() const = 0;

virtual string Tine() caonst = 0;

virtual void add (DIsplar char ch) = 0;

virtual void add(const String &s) = 0;

virtual void clear();

};

Пример 2

Class Gambler {

Public:

Virtual int move()=0;

};


 


Глава 8. Параметризованные функции и классы

Шаблоны функций.

Простейший шаблон функции имеет форму:

template <class Type> <заголовок>

{

<тело функции>

}

В общем случае шаблон функции может содержать несколько параметров, каждый из которых может быть не только типом, но и просто переменной.

Пример 1

template <class A, class B, int i> void f() {...}

Шаблон функции использует в качестве аргумента тип переменной.

Пример 2

template<class T> Tsgr_it(T x)

{

...

return x*x;

}

В шаблоне может использоваться необязательный первый тип как параметр

Пример 3

template<class T1, class T2>

Max(T1 x, T2 y)

{ return (x>y)? x: y; }

В C++ вызов шаблона функции, использующий конкретный тип данных, приводит к тому, что компилятор создает код соответствующей версии функции. Этот процесс называется инстанцированием. Конкретный тип для инстанцирования определяется компилятором автоматически, исходя из типов параметров при вызове функции, либо задается явным образом. При повторном вызове с тем же типом данных код заново не генерируется. Если на месте параметра шаблона находится не тип, а переменная, то должно указываться константное выражение.


Пример 4

Явное задание аргументов при вызове:

template<class x, class y, class z>void (x, y, z);


Void g()

{

f < int, char*

}

Как и обычные функции, шаблоны функций могут быть перегружены как с помощью шаблона, так и с помощью обычной функции. В программе можно предусмотреть специальную обработку.


 


Шаблоны классов

Шаблоны классов (параметризованные классы) рассматриваются как описание множества классов, моделирующих абстрактные структуры данных и отличающихся типом полей, включенных в эту структуру. Шаблоны классов определяют правила построения каждого отдельного класса из множества разрешенных (допустимых) классов. Синтаксис описания шаблона класса выглядит так:

Template <список параметров шаблона>. <описание класса>

В списке параметров шаблона могут быть как параметры, определяющие тип, так и параметры, для которых этот тип фиксирован. Каждый формальный параметр, определяющий тип, обозначается ключевым словом class. Используя шаблоны, можно определить объекты класса. Формат определения объекта одного из классов, порождаемых шаблоном, выглядит так:

Имя_парметра_класса <список параметров шаблона>


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: