Работа по С++ (1021520), страница 4
Текст из файла (страница 4)
Виртуальная функция объявляется в базовом или впроизводном классе и, затем, переопределяется в наследуемых классах. Совокупность классов (подклассов), в которых определяется и переопределяется виртуальная функция, называется полиморфическим кластером, ассоциированным с некоторой виртуальной функцией. В пределах полиморфического кластера сообщение связывается с конкретной виртуальной функцией-методом во время выполнения программы.
Обычную функцию-метод можно переопределить в наследуемых классах. Однако без атрибута virtual такая функция-метод будет связана с сообщением на этапе компиляции. Атрибут virtual гарантирует позднее связывание в пределах полиморфического кластера.
Часто возникает необходимость передачи сообщений объектам, принадлежащим разным классам в иерархии. В этом случае требуется реализация механизма позднего связывания. Чтобы добиться позднего связывания для объекта, его нужно объявить как указатель или ссылку на объект соответствующего класса. Для открытых производных классов указатели и ссылки на объекты этих классов совместимыми с указателями и ссылками на объекты базового класса (т.е. к объекту производного класса можно обращаться, как будто это объект базового класса). Выбранная функция-метод зависит от класса, на объект которого указывается, но не от типа указателя.
С++ поддерживает virtual функции-методы, которые объявлены в основном классе и переопределены в порожденном классе. Иерархия классов, определенная общим наследованием, создает связанный набор типов пользователя, на которые можно ссылаться с помощью указателя базового класса. При обращении к виртуальной функции через этот указатель в С++ выбирается соответствующее функциональное определение во время выполнения. Объект, на который указывается, должен содержать в себе информацию о типе, поскольку различия между ними может быть сделано динамически. Это особенность типична для ООП кода. Каждый объект “знает” как на него должны воздействовать. Эта форма полиморфизма называется чистым полиморфизмом.
В С++ функции-методы класса с различным числом и типом параметров есть действительно различные функции, даже если они имеют одно и тоже имя. Виртуальные функции позволяют переопределять в управляемом классе функции, введенные в базовом классе, даже если число и тип аргументов то же самое. Для виртуальных функций нельзя переопределять тип функции. Если две функции с одинаковым именем будут иметь различные аргументы, С++ будет считать их различными и проигнорирует механизм виртуальных функций. Виртуальная функция обязательно метод класса.
Задание к работе
Общая постановка. Программа должна содержать:
-
базовый класс Х, включающий два элемента х1, х2 типа int,
-
конструктор с параметрами для создания объектов в динамической области памяти,
-
деструктор,
-
виртуальные методы просмотра текущего состояния и переустановки объектов базового класса в новое состояние.
-
производный класс У, включающий один элемент у типа int ,
-
конструктор с параметрами и списком инициализаторов, передающий данные конструктору базового класса,
-
переопределенные методы просмотра текущего состояния объектов и их переустановки в новое состояние.
Варианты заданий
Создать в производном классе метод Run, определяющий:
-
Сумму компонент классов
-
Произведение компонент классов
-
Сумму квадратов компонент классов
-
Значение х1+х2 – у
-
Значение (х1+х2)/у
-
Значение (х1+х2)*у
-
Значение х1*у+х2
-
Значение х1+х2*у
-
Произведение квадратов компонент класса
-
Значение х1*х2+у
-
Значение х1*х2/у
-
Значение х1*х2-у
-
Значение (x1-x2)*y
-
Значение (x1-x2)/y
Контрольные вопросы
-
Что такое наследование, одиночное наследование, множественное наследование?
-
Какие объекты базового класса наследуются в производном, а какие нет?
-
На примере своей программы поясните механизм позднего связывания.
-
В каком случае С++ проигнорирует механизм виртуальных функций?
Работа № 6.
Программирование шаблона классов
Теоретические сведения
Задание к работе
Варианты заданий
Контрольные вопросы
Цель работы – изучить приемы создания и использования шаблонов классов.
Теоретические сведения
Достаточно часто встречаются классы, объекты которых должны содержать элементы данных произвольного типа (в том смысле, что их тип определяется отдельно для каждого конкретного объекта). В качестве примера можно привести любую структуру данных (массив указателей, массив, список, дерево). Для этого в С++ предлагаются средства, позволяющие определить некоторое множество идентичных классов с параметризованным типом внутренних элементов. Они представляют собой особого вида заготовку класса, в которой в виде параметра задан тип (класс) входящих в него внутренних элементов данных. При создании конкретного объекта необходимо дополнительно указать и конкретный тип внутренних элементов в качестве фактического параметра. Создание объекта сопровождается созданием соответствующего конкретного класса для типа, заданного в виде параметра. Принятый в С++ способ определения множества классов с параметризованным внутренним типом данных (иначе, макроопределение) называется шаблоном (template).
Синтаксис шаблона рассмотрим на примере шаблона класса векторов, содержащих динамический массив указателей на переменные заданного типа.
// <class T> - параметр шаблона - класс "T", внутренний тип данных
// vector - имя группы шаблонных классов
template <class T> class vector
{
int tsize; // Общее количество элементов
int csize; // Текущее количество элементов
T **obj; // Массив указателей на парам. объекты типа "T"
public:
T *operator[](int); // оператор [int] возвращает указатель на
// параметризованный объект класса "T"
void insert(T*); // включение указателя на объект типа "T"
int index(T*);
};
Данный шаблон может использоваться для порождения объектов-векторов, каждый из которых хранит объекты определенного типа. Имя класса при этом составляется из имени шаблона "vector" и имени типа данных (класса), который подставляется вместо параметра "Т":
vector<int> a;
vector<double> b;
extern class time;
vector<time> c;
Заметим, что транслятором при определении каждого вектора с новым типом объектов генерируется описание нового класса по заданному шаблону (естественно, неявно в процессе трансляции). Например, для типа int транслятор получит:
class vector<int>
{
int tsize;
int csize;
int **obj;
public:
int *operator[](int);
void insert(int*);
int index(int*);
};
Далее следует очевидное утверждение, что функции- методы шаблона также должны быть параметризированы, то есть генерироваться для каждого нового типа данных. Действительно, это так: функции-методы шаблона классов в свою очередь также являются шаблонными функциями с тем же самым параметром. То же самое касается переопределяемых операторов:
// параметр шаблона - класс "T", внутренний тип данных
// имя функции-элемента или оператора - параметризировано
//
template <class T> T* vector<T>::operator[](int n)
{
if (n >=tsize) return(NULL);
return (obj[n]);
}
template <class T> int vector<T>::index(T *pobj)
{
int n;
for (n=0; n<tsize; n++)
if (pobj == obj[n]) return(n);
return(-1);
}
Заметим, что транслятором при определении каждого вектора с новым типом объектов генерируется набор методов- функций по заданным шаблонам (естественно, неявно в процессе трансляции). При этом сами шаблонные функции должны размещаться в том же заголовочном файле, где размещается определение шаблона самого класса. Для типа int сгенерированные транслятором функции-методы будут выглядеть так:
int* vector<int>::operator[](int n)
{
if (n >=tsize) return(NULL);
return (obj[n]);
}
int vector<int>::index(int *pobj)
{
int n;
for (n=0; n<tsize; n++)
if (pobj == obj[n]) return(n);
return(-1);
}
Задание к работе
Общая постановка. Дано: число N и последовательность a1, a2, … aN
Создать шаблон класса, порождающий динамические одномерные массивы с элементами различных типов (вещественные, целочисленные, символьные и т.д.). Тип данных и результат являются параметрами по отношению к классу, программа должна иметь методы инициализации, конструктор, деструктор, метод просмотра значений созданного массива, согласно заданному алгоритму.
Варианты заданий
-
a1, (a1+a2), … ,(a1+a2+…+aN);
-
(a1*a1), (a1*a2), …, (a1*aN);
-
|a1|, |a1+a2|, …, |a1+a2+…aN|;
-
a1, -a1*a2, +a1*a2*a3, … ,(-1)N*a1*a2*…aN;
-
-a1, +a2, -a3, … , (-1)N*aN;
-
(a1+1), (a2+2) , (a3+3), …, (aN+N);
-
a1*1, a2*2, a3*3, , aN*N;
-
a1*a2, a2*a3, … , (aN-1)*aN;
-
a1/1, a2/2, a3/3, …,aN/N;
-
(a1+a2), (a2+a3),… ,(aN-1+aN);
-
(a1+a2+a3), (a2+a3+a4), (a3+a4+a5), … (aN-2+aN-1+aN);
-
(N+a1), ( N-1+a2), ,(1+aN);
-
(N*a1), ( (N-1)*a2), ,(1*aN);
-
a1/N, a2/N, … ,aN/1.
Контрольные вопросы
-
С какой целью используются шаблоны классов?
-
Какие существуют виды параметров шаблона класса?
-
В чем заключается особенность использования функций – методов шаблона?
-
Может ли использоваться шаблон для параметризированных объектов?
Работа № 7.
Множественное наследование с использованием абстрактных базовых классов, файлового ввода-вывода с применением потоков С++, функций обработки исключительных ситуаций
Теоретические сведения
Задание к работе
Варианты заданий
Контрольные вопросы
Цель работы – изучить методику создания множественного наследования, использование абстрактного базового класса, файловый ввод – вывод и использование функций обработки исключительных ситуаций.
Теоретические сведения
Абстрактные классы
Если базовый класс используется только для порождения производных классов, то виртуальные функции в базовом классе могут быть "пустыми", поскольку никогда не будут вызваны для объекта базового класса. Базовый класс в котором есть хотя бы одна такая функция, называется абстрактным. Виртуальные функции в определении класса обозначаются следующим образом:
class base
{
public:
virtual print()=0;
virtual get() =0;
};
Определять тела этих функций не требуется.