Коллоквиум_2 по главам 2, 3 и приложению – 21 декабря Вопросы в конце этих слайдов.
ГЛАВА 3. Библиотека стандартных шаблонов (БСШ). Контейнеры Рассказ о библиотеке стандартных шаблонов необходимо начать с признания того факта, что она представляет собой вершину искусства программирования и в ней используются самые изощренные свойства С++. Герберт Шилдт. Самоучитель С++, стр. 419
При программировании задач разного типа наиболее распространенными структурами представления данных являются массивы, строки, списки, стеки, множества, деревья и т. д. С появлением технологии ООП, включая такие понятия как классы-шаблоны и функции- шаблоны, возникло желание стандартизировать часто используемые структуры данных - их хранение и обработку – создав набор стандартных классов.
Этапы разработки Идеология их создания претерпела несколько этапов. 1. Сначала была создана иерархия классов (около 22 классов), в основе которой лежал абстрактный класс Object. Однако применение этих классов оказалось сложным, т. к. их можно было использовать в основном для обработки данных этой же иерархии или порожденных из них.
А, например, для применения класса Stack для целых чисел необходимо было из класса Object породить класс Integer, определив 5 виртуальных функций(!) вспомогательного характера.
2. Затем на основе понятия классашаблона фирмой Borland была создана библиотека стандартных классовшаблонов двух уровней - FDS (Fundamental Data Structure): векторы, списки и - ADT(Abstract Data Type): стеки, очереди, множества. template
3. С появлением версий языка C++ выше третьей распространение получила библиотека - БСШ STL ( Standard Template Library), построенная на основе классовшаблонов.
1. Контейнер. Структура БСШ. Или просто контейнер Контейнерный класс - это класс-шаблон, в котором типы данных объявлены произвольными, а набор операций и функций над ними фиксирован. Таким образом, можно сказать, что контейнер – это объект, предназначенный для хранения и обработки других объектов.
Структура БСШ Ядро БСШ состоит из следующих основных компонент: - контейнеров, - итераторов, - алгоритмов, - распределителей памяти.
Контейнеры - это классы-шаблоны. Для каждого из классов определен набор - конструкторов, - деструкторов, - характерных для класса операций и функций.
Итераторы - это объекты специальных классов, предназначенные для перебора объектов в контейнерах. По своей сути, итератор - это указатель на объекты контейнера. Для каждого классашаблона предназначен свой итератор.
Действительно, алгоритмы перебора разных объектов отличаются: - элементы массива выбираются обычно произвольно с помощью индекса (прямой доступ), - а в связанном списке используется последовательный доступ по полю связи, например, p = p->next.
Итераторы позволяют перебирать элементы с начала контейнера или с конца (обратные итераторы).
Алгоритмы - это набор функций-шаблонов, выполняющих однотипную обработку объектов разных контейнеров. Например, - сортировку (пирамидальную), - слияние, Всего 66 функций! - бинарный поиск, - поиск максимума и минимума, и т. д.
Распределители памяти - это объекты специального класса allocator, управляющие процессом выделения памяти для контейнеров.
Для использования того или иного контейнера надо подключить соответствующую библиотеку. Перечислим часто используемые контейнеры:
Назначение Заголовочный файл Degue Двусторонняя очередь (дек)
В библиотеку входит также не контейнерный класс string. Для использования функций-шаблонов библиотеки надо подключить заголовочный файл
Структура БСШ: Функции-шаблоны
2. Контейнер vector – динамический массив vector - самый популярный контейнер. Это массив объектов, основное и замечательное отличие которого от обычных массивов в том, что память, занимаемая массивом объектов, может меняться при выполнении программы. Для обычных массивов это невозможно: либо память выделяется статически при описании int a[100] , либо при выполнении программы a = new int [n], а затем изменяться не может(только освободить память в случае 2 и взять заново).
Конструкторы В классе vector имеется 4 конструктора: 1. vector() - конструктор по умолчанию – пустой вектор; vector
Последний конструктор 4. vector(объект_контейнер) – конструктор копирования vector
К элементам вектора можно обращаться по индексу или через итератор (указатель), как и для обычных массивов (a[i] или *(a+i) ). В классе vector перегружены операции сравнения == < <= != > >= и определено 33 ч/функции.
Назовем основные функции: size() - текущий размер массива, push_back(знач) - поместить значение в конец массива, size() растет, pop_back() - удаляет последний элемент, size() уменьшается, begin() - возвращает итератор начала вектора, end() - возвращает итератор конца вектора, insert(итератор, значение) - вставляет в массив значение перед элементом, указанным итератором, size() растет
далее erase(итератор) - удаляет элемент, на который указывает итератор, возвращает итератор элемента за удаленным, size() уменьшается clear() - удаляет все элементы вектора, size() = 0 empty() - возвращает истину, если вектор пуст, и ложь, если нет.
Замечание Во всех контейнерах набор перегруженных операций и членфункций практически одинаковый, кроме некоторых специфических для данного класса-шаблона.
Пример 1. #include
for(i = 0; i
Пример 2. #include
Пример 2 Но без *! vector
Пример 2 cout<<"n Осталось элементов “ <
3. Контейнер list - список Этот контейнер реализует двунаправленный линейный список, в котором используется последовательный доступ с начала или конца списка. Элемент списка имеет структуру predecessor предшествующий элемент
В классе имеется 4 конструктора, перегружены операции сравнения и определены 42 член-функции. Так как список двунаправленный, то добавление и удаление выполняется как с начала, так и с конца. Ниже на рисунке приведены основные операции для работы со списком
Член-функции: push_front(значение) - добавить в начало, push_back(значение) - добавить в конец, pop_front() - удалить с начала, pop_back() - удалить с конца, insert(итератор, значение) - вставить в середину, место указывается итератором, erase(итератор) - удалить из середины, место указывается итератором, front() – возвращается значение элемента с начала списка, back() - возвращается значение элемента с конца списка.
Пример. # include
list
cout<<"n rezult: "; for(pl = l 2. begin(); pl != l 2. end(); pl++) cout<<(*pl)<<' '; }
4. Контейнер Set - множество Контейнер Set определяет математическое множество объектов. Основное достоинство класса в том, что во множество не добавляются одинаковые объекты. Добавление во множество, в отличие от предыдущих контейнеров, выполняется функцией класса insert(значение).
Пример. #include
5. Не контейнерный класс string Объект типа string отличается от стандартного массива char * тем, что в этом классе перегружены, как операции, действия, которые для char * выполнялись только с помощью функций. Это операции = вместо strcpy, + , += вместо strcat , причем имеется по 3 перегруженных операции + char*, + char, + string ( аналогично для += ), == , !=, <, <=, >, >= вместо strcmp, [ ] индекс, при этом выполняется контроль за выходом индекса за границы строки, чего нет для базового char*, <<, >> - потоковый вывод и ввод строк.
Имеется также большой набор членфункций, позволяющих выполнять аналогичные действия над диапазонами строк (часть какой-либо строки присвоить текущей, или сравнить с текущей, или найти первое вхождение с начала строки или с конца, или добавить к текущей). Так класс string входит в БСШ, то к нему применимы и функции-шаблоны.
Некоторые член-функции length() – длина строки, int find (char *) – первое вхождение строки типа char *, int find(char )– первое вхождение символа int find(string) – первое вхождение строки типа string и др.
Конструкторы Определены 3 конструктора string() - пустая строка, может расти, string(const char *), string(const string&).
Пример. Выполнить конкатенацию строк. Фамилии берутся из массива строк по одной и из них формируется линейный список с помощью контейнера list. Затем фамилии последовательно добавляются в строку-результат, а из списка удаляются, пока он не опустеет.
# include
"; // формируем //" src="https://present5.com/presentation/3/49889970_140299646.pdf-img/49889970_140299646.pdf-46.jpg" alt="while(!ls. empty()) // пока список не пуст, {ss = ss+*ls. begin()+"<->"; // формируем //" />
while(!ls. empty()) // пока список не пуст, {ss = ss+*ls. begin()+"<->"; // формируем // строку из фамилий, ls. pop_front(); // удаляя по одной // фамилии c начала списка } cout<<"n"<
Решаем задачи на STL 1. Обернуть массив - используется итератор. Затем массив отсортировать. И вновь обернуть. Функции–шаблоны sort, reverse 2. В динамическом массиве найти первый 0 и последний. Числа между ними обнулить. Используется итератор. 3. Создать список фамилий. Удалить самую длинную. Член-функции list: : erase(итератор), string: : length()
4. Найти слово в предложении с мин количеством разных букв. Используем set. 5. Создать массив. Присвоить его другому. Упорядочить второй массив Сравнить их операцией == Слить 2 упорядоченных в третий упорядоченный. Функции-шаблоны sort, merge.
Коллоквиум_2 по главам 2, 3 и приложению – 21 декабря Вопросы в конце этих слайдов.
Приложение. Объект - факультет Person: : fio, adr, g_r Student: : + kurs, grup, spec Вначале пусты Faculty : : char * short_name, long_name; vector
// Наследование, агрегирование // Базовый класс Person class Person { friend class Faculty; protected: char *fio, *adr; int g_r; public: Person() // КОНСТРУКТОР ПО УМОЛЧАНИЮ { fio = new char[1]; adr = new char[1]; fio[0] = 0; adr[0] = 0; g_r = 0; }
КОНСТРУКТОР С АРГУМЕНТАМИ Person(char *f, char *a, int gr) { fio = new char[ strlen(f) +1 ]; strcpy(fio, f); adr = new char[ strlen(a) + 1]; strcpy(adr, a); g_r = gr; }
~Person(){ cout<<"n. Работает деструктор класса Person"; delete [] fio; delete [] adr; }
КОНСТРУКТОР КОПИРОВАНИЯ Person( Person &p ) { fio = new char[ strlen(p. fio) + 1]; adr = new char[ strlen(p. adr) + 1]; strcpy(fio, p. fio); strcpy(adr, p. adr); g_r = p. g_r; }
Перегрузка = Person & operator = (Person &p) {if( this != &p) {delete [] fio; delete [] adr; fio = new char[ strlen(p. fio) + 1]; adr = new char [ strlen(p. adr) + 1]; strcpy(fio, p. fio); strcpy(adr, p. adr); g_r = p. g_r; } return *this; }
Виртуальная In() virtual void In() { char buf 1[80], buf 2[80]; cout<<"n. ФИО? "; gets(buf 1); cout<<"Адрес? "; gets(buf 2); cout<<“Год рождения ? "; cin>>g_r; Person p(buf 1, buf 2, g_r); *this = p; }
Виртуальная Show() virtual void Show() {cout<<"n Фио: "<
// КЛАСС STUDENT, // ПОРОЖДЕННЫЙ из PERSON class Student : public Person { friend class Faculty; protected: int grup, kurs; char *spec; public: Student() : Person() // конструктор по умолч. { grup = kurs = 0; spec = new char[1]; spec[0] = 0; }
Конструктор с аргументами Student(char *f, char *adr, int gr, int kr, char *sp): Person(f, adr, g), grup(gr), kurs(kr) //список инициализации { spec = new char[ strlen(sp) + 1]; strcpy(spec, sp); } Конструктор порожденного класса отвечает за инициализацию ч/д базового
Конструктор копирования Обратите внимание, как используется конструктор Person: В него передается базовая часть s Student( Student &s): Person(s) { kurs = s. kurs; grup = s. grup; spec = new char[ strlen(s. spec) + 1]; strcpy(spec, s. spec); }
~Student() { cout<<"n. Работает деструктор класса Student"; delete []spec; }
operator = Student & operator = (Student &s) { if( this != &s) { Person : : operator = (s); delete [] spec; spec = new char [ strlen(s. spec) + 1]; strcpy(spec, s. spec); grup = s. grup; kurs = s. kurs; } return *this; }
Виртуальная Student : : In() void In() {cout<<"n. Ввод данных о студенте"; Person : : In(); char buf[80]; // fflush(stdin); cout<<"n Специальность ? "; gets(buf); delete []spec; spec = new char[ strlen(buf) + 1]; strcpy(spec, buf); cout<<"nкурс? "; cin>>kurs; cout<<“группа? "; cin>>grup; }
Потоковый ввод friend istream & operator >>( istream &r, Student &s) { s. In(); return r; }
Виртуальная Student : : Show() void Show() { Person : : Show(); cout<<"n курс - "<
// CLASS TEACHER, ПОРОЖДЕННЫЙ ИЗ PERSON // class Teacher : public Person { friend class Faculty; char *kafedra, *prof; public:
Конструкторы Teacher() // по умолчанию {// самостоятельно } Teacher(char *f, char *a, int gr, char *kf, char *pr): // конструктор с аргументами { // самостоятельно } Teacher(Teacher &t): // конструктор // копирования {/* самостоятельно */ }
И так до конца - самостоятельно Teacher& operator = (Teacher &); ~Teacher() ; virtual void In() {cout<<"n. Ввод данных о сотрудникеn"; // … } void Show() ; /* Определите операторы потокового ввода >>, вывода << */ }; // End Teacher
// КЛАСС УСПЕВАЕМОСТЬ, ПОРОЖДЕННЫЙ ИЗ STUDENT // class Usp : public Student { int ball[4]; // для простоты 4 оценки public:
Конструкторы Usp() : Person(), Student()//конструктор по умолч. { ball[0] = ball[1] = ball[2] = ball[3] = 0; }
Конструктор с аргументами Usp(Student &s, int *b, int k); /* Конструктор: занесениe k оценок из массива b студенту s */
Конструктор нестандартного преобразования Usp (Student &s) : Student(s) / * не стандартное преобразование базового к порожденному для первоначального занесения в журнал успеваемости студентов без оценок из объекта "факультет". Т. к. информация в журнале об адресе и специальности лишняя, удаляем ее */ { delete [] adr; delete [] spec; adr = new char[1]; adr[0] = 0; spec = new char[1]; spec[0] = 0; ball[0] = ball[1] = ball[2] = ball[3] = 0; }
Виртуальная Usp: : In() void In() // ввод данных о студенте и его оценок {char buf[80]; /* в проинициализированный по умолчанию журнал */ cout<<"n ФИО? "; gets(buf); delete [] fio; fio = new char [strlen(buf) + 1]; strcpy(fio, buf); cout<<"n Курс? "; cin>>kurs; cout<<"n. Группа? "; cin>>grup; cout<<"n Оценки: "; cin>>ball[0]>>ball[1]>>ball[2]>>ball[3]; //Если оценок нет, то 0 0 }
Перегруженная USP: : In() void In(char *f, int b, int j) // перегруженная /* ввод оценки b с номером j студенту по фамилии f для дополнения или изменения оценки в журнале. Может использоваться в цикле по журналу для занесения оценки одного студента */ { if(strcmp(f, fio)) return; ball[ j - 1] = b; } /* пользователю привычней считать с 1 */
Виртуальная Usp: : Show() void Show(); // виртуальная }; // End Usp void Usp : : Show() { cout<< "n"<
Конструктор Usp: : Usp(Student &s, int *b, int k) : Student(s) { for (int i =0; i
// CLASS FACULTY, АГРЕГИРОВАННЫЙ ИЗ STUDENT&TEACHER // class Faculty { vector
~Faculty(); // Самостоятельно Faculty(Faculty &f) { s = f. s; t = f. t; long_name = new char[strlen(f. long_name)+1]; strcpy(long_name, f. long_name); short_name = new char[strlen(f. short_name)+1]; strcpy(short_name, f. short_name); } Faculty operator = (Faculty &f); //самостоятельно
Добавление void operator += (Student &st) // добавить студента { s. push_back(st); } void operator += (Teacher &tc) // добавить преподавателя {/*самостоятельно */}
Удаление void operator -= (char *fam) // найти студента по фамилии fam и удалить { vector
Функции вывода самостоятельно void Show(int g); // вывод фио студентов группы // g void Show(); /* ВЫВОД ВСЕГО СОСТАВА ФАКУЛЬТЕТАфио и кафедра(или группа) */ void Show_s(); /* Вывод списка студентов факультета - только фио и группа */ void Show_t(); /* Вывод списка преподавателей факультета - только фио и кафедра */ void Show_k(int k); // вывод фио студентов курса k void Show(char *kf); /*вывод преподавателей кафедры kf – фио и должность */
Вернуть… Student & Gets(int i) // вернуть i-го студента(с контролем) { if( i<0 || i>=s. size()) { char p[5]; sprintf(p, ” %d”, i); throw strcat( "Нет студента с номером “, p); } return s[i]; } Teacher& Gett(int i) // вернуть i-го преподавателя (с контролем ) { /* самостоятельно*/ }
Вернуть… Student& Gets(char *fio); // Найти и вернуть студента по фамилии fio Teacher & Gett(char *fio) /* Найти и вернуть преподавателя по фамилии fio */ { /* самостоятельно */ } int Count_s() // вернуть количество студентов { return s. size(); } int Count_t() // вернуть количество преподавателей { return t. size(); } }; // End Faculty
Student & Faculty: : Gets(char *fio) { for(int i = 0; i
// MAIN() // void main() // Персонажи вымышлены. // Любые совпадения с реальными //людьми - случайны. { try{
// студенты Student s 1("Петелин А", "Томск, Кирова, 2, кв. 3", 1989, 1153, 5, "Экономист"), s 2("Пак А", "Томск, Усова 3 -4", 1990, 1162, 4, "Прикладник"), s 3( «Климов Е", "Красноярск Мира 205 -12", 1990, 1185, 2, “Защитник"), s 4;
Преподаватели Teacher t 1("Петров ПП", "Томск, Ленина , 36, 4 -02", 1950, "ТВи. МС", "профессор"), t 0(“Путин В", ”Томск Лыткина 7 -304", 1955, "ПМ", "Доцент");
Начинаем работать s 1. Show(); Student s 33 = s 3; /*Работает конструктор копирования */ s 33. Show(); /*невиртуальный вызов функции Student: : Show()*/ cin>>s 4; /* перегруженный ввод для Student */
Продолжаем работать Person *p; p = &s 2; p->Show(); /* виртуальный вызов функции Student: : Show() */ p = &t 1; p->Show(); /* виртуальный вызов функции Teacher: : Show() */
Работаем с факультетом Faculty fpmk("Факультет ПМ и К", "ФПМК"); fpmk += s 1; fpmk += s 2; fpmk += s 33; fpmk += t 0; fpmk += t 1; cout<<"nsize ФПМК: “<
fpmk. Show(1185); //Вывод студентов гр. 1185 fpmk. Show_k(2); // Вывод студентов 2 курса; fpmk. Show("ПМ"); /* Вывод сотрудников кафедры 'Прикладная Математика‘*/ Student st; // Работает конструктор по умолчанию st. In(); // Невиртуальный вызов Student: : In() fpmk += st; st. Show(); /* Невиртуальный вызов Student: : Show()*/ Teacher tt; p = &tt; p->In(); // Виртуальный вызов Teacher: : In()
fpmk += tt; p->Show(); /* Виртуальный вызов Teacher: : Show()*/ fpmk -= “Пак А"; // удаляем студента fpmk - "Сибирякова ВА"; /*Сообщение "Преподаватель Сибирякова не найден“ */ Student s 0 = fpmk. Gets(“Климов Е"); s 0. Show(); // невиртуальный вывод fpmk. Show_s(); // Вывод студентов ФПМК
Работаем с успеваемостью int b[4] = {5, 5, 4, 5}; Usp u(s 2, b, 4); // успеваемость Пак А p = &u; p->Show(); // виртуальный вызов Usp: : Show()
The End }catch( char*s) {puts(s); } catch(. . . ){puts("others!"); } getch(); }
Коллоквиум_2 по главам 2, 3 и приложению – 21 декабря Вопросы в конце этих слайдов. Классы приложения будут на распечатках. Изучите приложение самостоятельно.
Коллоквиум_2 по главам 2, 3 и приложению – 21 декабря Вопросы к экзамену по ЯП и МТ 1. Базовый и порожденный классы. Тип доступа protected. Типы наследования public, protected и private. Ограничения наследования. 2. Простое наследование. Принцип доминирования в иерархии наследования. 3. Конструктор порожденного класса. Его вид. 4. Стандартные преобразования при наследовании. 5. Множественное наследование. Прямые и не прямые базовые классы. Виртуальный базовый класс. Особенности инициализации его ч/данных.
6. Пример множественного наследования. Объяснить на примере программы kolobok. cpp 7. Полиморфизм. Раннее и позднее связывание. 8. Полиморфизм: виртуальные функции. Чистые виртуальные функции и абстрактный базовый класс. 9. 8 правил определения виртуальных функций. 10. Механизм позднего связывания. 11. Пример виртуальной функции - виртуальная функция Draw(). Объяснять на примере программы virtdraw. cpp
12. Библиотека STL - БСШ. Определение контейнера. Структура БСШ. 13. Часто используемые контейнеры: vector, list, set. Простые примеры их использования. 14. Классы Person, Student, Teacher, Usp, Faculty. Иерархия этих классов. Конструкторы, деструкторы. Виртуальные функции. Функции класса Faculty. Уметь приводить примеры использования этих классов. З А М Е Ч А Н И Е 1. Указанные в п. 14 классы будут в вопросах билетов, как задачи. ЗАМЕЧАНИЕ 2. Изложение ответов на все вопросы сопровождать ПРИМЕРАМИ.