
- •Учебное пособие
- •Введение
- •Объектно-ориентированный подход
- •Объектно-ориентированное программирование Абстрактные типы данных
- •Базовые принципы объектно-ориентированного программирования
- •Простейший ввод и вывод
- •Объект cout
- •Манипуляторы hex и oct
- •Другие манипуляторы
- •Объект cin
- •Операторы для динамического выделения и освобождения памяти (new и delete)
- •Базовые конструкции объектно-ориентированных программ Объекты
- •Понятие класса
- •Конструктор копирования
- •Конструктор explicit
- •Указатели на компоненты класса
- •Встроенные функции (спецификатор inline)
- •Организация внешнего доступа к локальным компонентам класса (спецификатор friend)
- •Вложенные классы
- •Static-члены (данные) класса
- •Указатель this
- •Компоненты-функции static и const
- •Proxi-классы
- •Параметры ссылки
- •Независимые ссылки
- •Практические приемы ограничения числа объектов класса
- •Наследование (производные классы)
- •Конструкторы и деструкторы при наследовании
- •Виртуальные функции
- •Абстрактные классы
- •Виртуальные деструкторы
- •Множественное наследование
- •Виртуальное наследование
- •Перегрузка функций
- •Перегрузка операторов
- •Перегрузка бинарного оператора
- •Перегрузка унарного оператора
- •Дружественная функция operator
- •Перегрузка оператора []
- •Перегрузка оператора ()
- •Перегрузка операторов new и delete
- •Преобразование типа
- •Явные преобразования типов
- •Преобразования типов, определенных в программе
- •Шаблоны Параметризированные классы
- •Передача в шаблон класса дополнительных параметров
- •Шаблоны функций
- •Совместное использование шаблонов и наследования
- •Шаблоны класса и friend
- •Некоторые примеры использования шаблона класса Реализация smart-указателя
- •Классы поддерживающие транзакции
- •Задание значений параметров класса по умолчанию
- •Пространства имен
- •Ключевое слово using как директива
- •Ключевое слово using как объявление
- •Псевдоним пространства имен
- •Организация ввода-вывода
- •Состояние потока
- •Строковые потоки
- •Организация работы с файлами
- •Организация файла последовательного доступа
- •Создание файла произвольного доступа
- •Основные функции классов ios, istream, ostream
- •Основы обработки исключительных ситуаций
- •Перенаправление исключительных ситуаций
- •Исключительная ситуация, генерируемая оператором new
- •Генерация исключений в конструкторах
- •Задание собственной функции завершения
- •Спецификации исключительных ситуаций
- •Задание собственного неожиданного обработчика
- •Иерархия исключений стандартной библиотеки
- •Стандартная библиотека шаблонов (stl) Общее понятие о контейнере
- •Общее понятие об итераторе
- •Категории итераторов
- •Основные итераторы
- •Вспомогательные итераторы
- •Операции с итераторами
- •Контейнерные классы Контейнеры последовательностей
- •Контейнер последовательностей vector
- •Контейнер последовательностей list
- •Контейнер последовательностей deque
- •Ассоциативные контейнеры
- •Ассоциативный контейнер multiset
- •Ассоциативный контейнер set
- •Ассоциативный контейнер multimap
- •Ассоциативный контейнер map
- •Адаптеры контейнеров
- •Адаптеры stack
- •Адаптеры queue
- •Адаптеры priority_queue
- •Пассивные и активные итераторы
- •Алгоритмы
- •Алгоритмы сортировки sort, partial_sort, sort_heap
- •Алгоритмы поиска find, find_if, find_end, binary_search
- •Алгоритмы fill, fill_n, generate и generate_n
- •Алгоритмы equal, mismatch и lexicographical_compare
- •Математические алгоритмы
- •Алгоритмы работы с множествами
- •Алгоритмы swap, iter_swap и swap_ranges
- •Алгоритмы copy, copy_backward, merge, unique и reverse
- •Примеры реализации контейнерных классов Связанные списки
- •Реализация односвязного списка
- •Реализация двусвязного списка
- •Реализация двоичного дерева
- •Литература
- •Вопросы по курсу ооп
- •220013, Минск, п.Бровки, 6.
Конструкторы и деструкторы при наследовании
Инструкция avt av("книга 1",123," автор 1") в примере программы предыдущего пункта приводит к формированию объекта av и вызова конструктора avt производного класса и конструктора book базового класса (предыдущая программа):
void avt::avt(char *s1,int i,char *s2) : book(s1,i)
При этом вначале вызывается конструктор базового класса book (выполняется инициализация компонент-данных naz и kl), затем конструктор производного класса avt (инициализация компоненты fm). Поскольку базовый класс ничего не знает про производные от него классы, его инициализация (вызов его конструктора) производится перед инициализацией (активизацией конструктора) производного класса.
В противоположность этому деструктор производного класса вызывается перед вызовом деструктора базового класса. Это объясняется тем, что уничтожение объекта базового класса влечет за собой уничтожение и объекта производного класса, следовательно, деструктор производного класса должен выполняться перед деструктором базового класса.
Ниже приведена программа вычисления суммы двух налогов, в которой использована следующая форма записи конструктора базового и производного классов.
имя_конструктора(тип переменной_1 имя_переменной_1,…,
тип переменной_n имя_переменной_n) :
имя_конструктора_базового_класса(имя_переменной_1,…, имя_переменной_k),
компонент_данное_1(имя_переменной_m),…,
компонент_данное_n(имя_переменной_n);
#include "iostream.h"
class A // базовый класс
{ protected:double pr1,pr2; // protected для видимости pr в классе B
public:
A(double prc1,double prc2): pr1(prc1),pr2(prc2) {};
void a_prnt(){cout << "% налога = " << pr1 << " и " << pr2 << endl;}
};
class B : public A // производный класс
{ int sm;
public:
B(double prc1,double prc2,int sum): A(prc1,prc2),sm(sum) {};
void b_prnt()
{ cout << " налоги на сумму = " << sm << endl;
cout << "первый = " << pr1 <<"\n втрой = " << pr2 << endl;
}
double rashet() {return pr1*sm/100+pr2*sm/100;}
};
void main()
{ A aa(9,5.3); // описание объекта аа (базового класса) и инициа-
// лизация его компонент с использованием
// конструктора А()
B bb(7.5,5,25000); // описание объекта bb (производного класса)
// и инициализация его компонент (вызов конструк-
// тора B() и конструктора А() (первым))
aa.a_prnt();
bb.b_prnt();
cout << "Сумма налога = " << bb.rashet() << endl;
}
В приведенном примере использованы функции-конструкторы следующего вида:
public: A(double prc1,double prc2): pr1(prc1),pr2(prc2) {};
public: B(double prc1,double prc2,int sum): A(prc1,prc2),sm(sum) {};
Конструктор А считывает из стека 2 double значения prc1 и prc2, которые далее используются для инициализации компонент класса А pr1(prc1),pr2(prc2). Аналогично конструктор В считывает из стека 2 double значения prc1 и prc2 и одно значение int, после чего вызывается конструктор класса A(prc1,prc2), затем выполняется инициализация компоненты sm класса В.
Производный класс может служить базовым классом для создания следующего производного класса. При этом, как отмечалось выше, конструкторы выполняются в порядке наследования, а деструкторы в обратном порядке. Если при наследовании переопределена хотя бы одна перегруженная функция, то все остальные варианты этой функции будут скрыты. Если необходимо, чтобы они оставались доступными в производном классе, все их надо переопределить. Если в производном классе создается функция с тем же именем, типом возвращаемого значения и сигнатурой, как и у функции–компоненты базового класса, но с новой реализацией, то эта функция считается переопределенной. Под сигнатурой понимают имя функции со списком параметров, заданных в ее прототипе, а также слово const. Типы возвращаемых значений могут различаться. Распространенная ошибка: пытаясь переопределить функцию базового класса, забывают включить слово const, в результате чего функция оказывается скрыта, а не переопределена.
Ниже приведен текст еще одной программы, в которой также используется наследование. В программе выполняется преобразование арифметического выражения из обычной (инфиксной) формы в форму польской записи. Для этого используется стек, в который заносятся арифметические операции. Алгоритм преобразования выражения здесь не рассматривается.
#include<iostream.h>
#include<stdlib.h>
class st // описание класса – элемент стека операций
{ public :
char c;
st *next;
public:
st(){} // конструктор
~st(){} // деструктор
};
class cl : public st
{ char *a; // исходная строка (для анализа)
char outstr[80]; // выходная строка
public :
cl() : st() {} // конструктор
~cl(){} // деструктор
st *push(st *,char); // занесение символа в стек
char pop(st **); // извлечение символа из стека
int PRIOR(char); // определение приоритета операции
char *ANALIZ(char *); // преобразование в польскую запись
};
char * cl::ANALIZ(char *aa)
{ st *OPERS; //
OPERS=NULL; // стек операций пуст
int k,p;
a=aa;
k=p=0;
while(a[k]!='\0'&&a[k]!='=') // пока не дойдем до символа '='
{ if(a[k]==')') // если очередной символ ')'
{ while((c=pop(&OPERS))!='(') // считываем из стека в выходную
outstr[p++]=c; // строку все знаки операций до символа
// ‘(‘ и удаляем из стека ‘(‘
}
if(a[k]>='a'&&a[k]<='z') // если символ – буква, то
outstr[p++]=a[k]; // заносим ее в выходную строку
if(a[k]=='(') // если очередной символ '(' , то
OPERS=push(OPERS,'('); // помещаем его в стек
if(a[k]=='+'||a[k]=='-'||a[k]=='/'||a[k]=='*')
{ // если следующий символ – знак операции, то
while((OPERS!=NULL)&&(PRIOR(c)>=PRIOR(a[k])))
outstr[p++]=pop(&OPERS); // переписываем в выходную строку все
// находящиеся в стеке операции с большим
// или равным приоритетом
OPERS=push(OPERS,a[k]); // записываем в стек очередную операцию
}
k++; // переход к следующему символу выходной строки
}
while(OPERS!=NULL) // после анализа всего выражения
outstr[p++]=pop(&OPERS); // переписываем операции из стека
outstr[p]='\0'; // в выходную строку
return outstr;
}
st *cl::push(st *head,char a) // функция записи символа в стек и возврата
{ st *PTR; // указателя на вершину стека
if(!(PTR=new st))
{ cout << "\n недостаточно памяти для элемента стека"; exit(-1);}
PTR->c=a; // инициализация элемента стека
PTR->next=head;
return PTR; // PTR – вершина стека
}
char cl::pop(st **head) // функция удаления символа с вершины стека
{ st *PTR; // возвращает символ (с вершины стека) и коррек-
// тирует указатель на вершину стека
char a;
if(!(*head)) return '\0'; // если стек пуст, то возвращается ‘\0'
PTR=*head; // адрес вершины стека
a=PTR->c; // считывается содержимое с вершины стека
*head=PTR->next; // изменяем адрес вершины стека (nex==PTR->next)
delete PTR;
return a;
}
int cl::PRIOR(char a) // функция возвращает приоритет операции
{ switch(a)
{ case '*':case '/':return 3;
case '-':case '+':return 2;
case '(':return 1;
} return 0;
}
void main()
{ char a[80]; // исходная строка
cl cls;
cout << "\nВведите выражение (в конце символ '='): ";
cin >> a;
cout << cls.ANALIZ(a) << endl;
}
В результате работы программы получим:
Введите выражение (в конце символ '=') : (a+b)-c*d=
ab+cd*-