Тема 9. Работа в объектно-ориентированной среде.



Объектно ориентированный код во всех языках программирования имеет некоторые общие особенности, имеется семь общих стандартных характеристик истинных объектно-ориентированных программ:

§ Модульность на базе объектов

§ Абстрактные типы данных

§ Автоматическое управление памятью

§ Классы

§ Наследование

§ Полиморфизм

Объектно-ориентированные стековые операции.

Рассмотрим пример класса С++, реализующего стековые операции.

Стеком называется связный список объектов с доступом в одной точке — вершине стека. Добавлять или извлекать элементы стека можно только через его вершину. Таким образом последний добавленный элемент будет извлечен первым.

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

В классе имеется шесть методов для управления стеком: clear() - очистка, top() - определение верхнего элемента, isempty() - проверка стека на пустоту, isfull() - проверка стека на полное заполнение, push() - извлечение из стека, pop() - заталкивание в стек.

#include <string.h>

#include <iostream.h>

 

class stack

{

       enum {maxlen=80};

       char str1[maxlen];

       int first;

public:

       void clear(){first=0;}

       char top(){return str1[first];}

       int isempty(){return (first==0);}

       int isfull(){return (first==maxlen-1);}

       void push(char chr){str[++first]=chr;}

       char pop(){return (str1[first--]);}

};

main()

{

       stack mystack;

       char str[11]=”0123456789”;

       mystack.clear();

       int i;

       // Load string to stack:

       for(i=0;i<strlen(str);i++)

       {

             if( !mystack.isfull() )

                   mystack.push(str[i]);

             cout<<str[i]<<endl;

       }

       // Unload characters from stack

       while(!mystac.isempty())

             cout<<mystack.pop()<<endl;

       return 0;

}

Загрузка и выгрузка выполняются через верхушку стека, поэтому первый записываемый символ оказывается наиболее глубоко.

Объектно-ориентированные связанные списки.

Связным списком называется связный набор элементов со связями, устанавливающими порядок следования объектов в списке. Первый элемент списка называют головой списка, последний — хвостом. Наиболее часто рассматривают односвязный список, в котором устанавливаются связи типа "Следующий", и двусвязный список со связями типа "Предыдущий‑Следующий". Доступ к элементам списка осуществляется посредством операций определения предыдущего и последующего элемента по уже известному (например, по голове или хвосту).

Для реализации концепции связанного списка необходимы все характеристики ООП.

Рассмотрим пример, создающий информационный список сотрудников.

Родительский класс nnr содержит сведения, общие для всех порождаемых классов-потомков (фамилия и имя сотрудника, должность, номер социальной страховки и стаж работы), они содержатся в разделе protected. Класс использует дружественный класс payroll_list:

class nnr

{

friend class payroll_list;

protected:

       char lstname[20];

       char fstname[15];

       char job_title[30];

       char sosial_sec[12];

       int year_hired;

       nnr *pointer;

       nnr *next_link;

public:

       nnr(…){…next_link=0;}

       void send_info();

       …

};

На основе класс nnr строятся классы-потомки, один из них salesperson:

class salesperson:publuc nnr

{

friend class payroll_list;

private:

       float sales;

       int comm_rate;

public:

       salesperson(…):nnr(…){sales=…;}

void fill_sales(float d_sales)

{sales=d_sales;}

void fill_comm_rate(int d_rate)

{comm_rate=d_rate;}

       void add_info(){pointer=this;}

       void send_info()

       {

             nnr::send_info();

             cout<<”sales ”<<sales<<endl;

             cout<<”Commision ”<<comm_rate;

       }

};

 

 

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

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

В дружественном классе payroll_list содержатся средства для печати связанного списка, добавления и удаления записей в списке:

class payroll_list

{

private:

       nnr *location;

public:

payroll_list(){location=0;}

void print_payroll_list();

void insert_employee(nnr *node);

void remove_employee(char *social_sec);

};

Рассмотрим функцию print_payroll_list(). Сначала в ней значение указателя на список присваиваивается переменной present . если указатель не нулевой, то в списке есть еще записи, которые передаются функции send_info(). Далее указатель смещается, и процесс повторяется:

void payroll_list::print_payroll_list()

{

       nnr *present=location;

       while(present!=0)

       {

             present_>send_info();

             present=present_>next_link;

       }

}

Переменная pointer содержит адрес памяти, где находится данный представитель класса. Это значение используется в функции insert_employee() для реализации связи между записями связного списка. При добавлении записи располагаются в алфавитном порядке по фамилиям сотрудников. Когда находится уже имеющаяся в списке фамилия (node_>lstname), большая, чем current_node_>lstname, то первый цикл заканчивается. Это стандартная процедура вставки в связанный список: указатель previous_node ссылается на запись, после которой вставляется новая запись, а current_node указывает на запись, которая будет следовать за вновь добавленной записью.

Когда точка вставки определена, программа создает новую связь или узел путем вызова функции node_>add_info(). Указатель current_node связывается с указателем next_link этого нового узла. Кроме того, проводится проверка на начало списка:

void payroll_list::insert_employee(nnr* node)

{

       nnr *current_node=location;

       nnr *previoise_node=0;

       while(current_node!=0 &&

             strcmp(current_node->lstrname,node->lstrname)<0 )

       {

             previous_node=current_node;

             current_node=current_node->next_link;

       }

node->add_info();

node->pointer->next_link=current_node;

if(previouse_node==0)

location=node->pointer;

else

previouse_node->next_link=node->pointer;

}

При удалении из связного списка используется номер социальной страховки для исключения возможности ошибочного удаления. Алгоритм удаления аналогичен предыдущему:

void payroll_list::remove_employee(char* social_sec)

{

       nnr *current_node=location;

       nnr *previoise_node=0;

       while(current_node!=0 &&

             strcmp(current_node->social_sec,social_sec)!=0 )

       {

             previous_node=current_node;

             current_node=current_node->next_link;

       }

if(current_node !=0 && previouse_node==0)

{

location=current_node->nex_link;

delete current_node;

}else

if(current_node !=0 && previouse_node!=0)

{

previouse_node->next_link=current_node->next_link;

delete current_node;

}

}

 

Первый условный оператор соответствует началу списка. В этом случае указатель начала списка перемещается к следующему элементу. Второй оператор if используется для удаления промежуточного узла. Для этого более удаленному узлу присваиваивается адрес узла, который следует за удаляемым.

Практическое задание.

 

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

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

Вариант A.

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

Разработайте класс "Динамически изменяемая очередь" и напишите программу, рисующую червяка, случайным образом ползающего по экрану. Каждая точка тела червяка повторяет траекторию головы. Предусмотрите возможности сохранения очереди на диске и восстановления его из файла.

Вариант B.

Деревом называется структура данных, основанная на порядке типа "Отец‑Сыновья". Дерево состоит из узлов. Каждый узел (кроме одного) имеет единственного родителя и одного или нескольких сыновей. Родители и сыновья, в свою очередь, также являются узлами и т.д. Дерево имеет также единственный узел, не имеющий родителя, но имеющий сыновей. Таким образом, все остальные узлы дерева являются прямыми или косвенными сыновьями этого узла. Такой узел называется корнем дерева. Узлы, имеющие и родителей и сыновей, часто называют ветвями дерева, а узлы, имеющие только родителей, но не имеющие сыновей называют листьями. По максимальному количеству сыновей у одного узла различают бинарные — 2 сына, тринарные — 3 сына, тетрарные — 4 сына и т.д. деревья.

Разработайте класс "Тринарное дерево" и напишите на его основе программу, строящую плоский фрактал по следующему рекуррентному алгоритму:

                    

           

Предусмотрите операции спасения дерева на диск и восстановления его с диска. Глубину прорисовки фрактала определите в программе исходя из количества свободной памяти в системе.

Вариант C.

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

Разработайте класс "Ассоциативный массив" и напишите программу рисующую на экране случайные фигуры с цветом, который пользователь задает по имени. Исходное состояние ассоциативного массива прочитайте из файла. Предусмотрите также процедуру сохранения ассоциативного массива на диске.

 

Вариант D.

Упорядоченным списком называется список объектов, в котором порядок следования элементов определяется из соотношений "больше‑меньше" некоторого заданного параметра (ключевой параметр). В этом случае первому элементу списка будет соответствовать элемент с наименьшим значением ключевого параметра, второй — с ключом большим, чем у первого, третий — с большим, чем у второго и т.д.

Разработайте класс "Упорядоченный список" и напишите программу, строящую упорядоченную круговую диаграмму по заданному набору величин. Предусмотрите возможности сохранения упорядоченного списка на диске и восстановления его из файла.

 

Вариант E.

Разработайте класс "Двусвязный список" и напишите программу создающую список из последовательных квадратов и пробегающую по этому списку от головы к хвосту и обратно. Текущий элемент списка выделяйте цветом. Предусмотрите возможности сохранения списка на диске и восстановления его из файла.

 

Вариант F.

Разработайте класс "Динамически изменяемый стек" и напишите программу, создающую набор произвольных объектов (например, букв на экране) и уничтожающую их в обратном порядке. Предусмотрите возможности сохранения стека на диске и восстановления его из файла.

 

Вариант G.

Разработайте класс "Динамически изменяемая очередь" и напишите программу, создающую набор произвольных объектов (например, окружностей на экране) и уничтожающую их в том же порядке, в котором объекты создавались. Предусмотрите возможности сохранения очереди на диске и восстановления его из файла.

 

Тема 10. Полнофункциональный ввод-вывод в С++.

Особенности ввода-вывод в языке С++.

В языке С++ ввод/вывод описывается как набор классов, описанный в заголовочном файле iostream.h. Аналогами потоков stdin, stdout, stderr являются классы cin, cout и cerr. Эти три потока открываются автоматически. Поток cin связан с клавиатурой, а cout, cerr - с дисплеем.

 Классы iostream используют перегруженные операции “<<” для записи (занесения в поток) и операциb “>>” для чтения (извлечения из потока). Операции возвращают ссылку на тип iostream, что позволяет сцеплять потоковые операции. Они перегружаются для всех типов данных (в том числе и для пользовательских), тем самым необходимость проверки соответствия типов отпадает. Компилятор самостоятельно выбирает необходимую функцию в соответствии с типом данных:

int ivalue;

float fvalue;

char c;

cin>>ivalue>>fvalue>>c;

cout<<”integer: “<<ivalue<<endl<<” float: “<<fvalue<<”char: “<<c;

Для вставки символа перевода строки необходимо либо выводить символ ‘\n’, либо константу endl.

Для форматирования выводимых данных для объектов iostream включены следующие методы:

precision(int p) - количество знаков после запятой,

width(int w) - размер поля для вывода,

setf(long manip) - установка флагов, определяющих формат вывода(ios::dec, ios::oct, ios::hex, ios::fixed, ios::scientific, ios::left, ios::right).

Классы iostream.

Все объекты ввода/вывода, описанные в библиотеке iostream, используют оди и тот же базовый класс ios (за исключением классов буферизованных потоков). Эти производные классы делятся на 4 категории.

Классы потокового ввода

istream Универсальный класс ввода, или родительский класс для других производных потоковых классов ввода.
ifstream Ввод из файлов.
istream_withassign Ввод из потока cin.
istrstream Ввод из строки.

Классы потокового вывода

ostream Универсальный класс вывода, или родительский класс для других производных потоковых классов вывода.
ofstream Вывод в файлы.
ostream_withassign Вывод в потоки cout, cerr и clog.
ostrstream Вывод в строку.

Классы потокового ввода/вывода

iщstream Универсальный класс ввода/вывода, или родительский класс для других производных потоковых классов ввода/вывода.
fstream Ввод/вывод в файлы.
stdiostream Стандартный поток ввода/вывода.
strstream Ввод/вывод в строку.

Классы буферизованных потоков

streambuf Родительский класс для производных буферизованных классов.
filebuf Буферизованный поток для файлов.
stdiobuf Буферизованный поток для стандартного файлового ввода/вывода.
strstreambuf Буферизованный поток для строк.

 Все производные от ios используется объект класса streambuf.

Потоковый ввод-вывод.

Используемый в следующем примере класс ifsteam :

#include <fstream.h>

void main()

{

       char one_line[80];

ifstream my_input_stream(“iputfile.cpp”,ios::in);

while(my_input_stream)

{

my_input_stream.getline(one_line,sizeof(one_line),’*’);

cout<<endl<<one_line;

}

my_input_stream.close();

}

Для объекта my_input_stream можно использовать методы open(), rdbuf() из класса ifstream и ряд методов из родительского класса istream: gcount(),get(), getline(), ignore(), peek(), putback(), read(), seekg(), tellg().

При создании объекта вызывается конструктор, которому передается имя файла, а также один или несколько режимов ввода, задаваемые константами, предопределенными в классе ios (ios::in ios::binary ios::nocreate). Константы объединяются с помощью операции побитового или “|”. Значение дескриптора файла можно использовать в логических проверках. При достижении конца файла оно устанавливается в ноль (условие EOF).

Метод getline() читает текстовые строки из входного потока (знак “*” является разделителем строк).

 

Классы ofstream позволяют выполнять вывод в поток streambuf:

#include <fstream.h>

#include <string.h>

void main()

{

       int i=0;

       long ltellp;

char sample[40]=”Sample string\n”;

       ofstream my_out_stream(“outfile.out”,ios::out);

       while(sample[i]!=0)

       {

             my_out_stream.put(sample[i]);

ltellp=my_out_stream.tellp();

cout<<”\ntellp value: “<<ltellp;

i++;

}

my_out_stream.write(sample,strlen(sample));

ltellp.my_out_stream.tellp();

cout<<”\ntellp after write: “<<ltellp;

my_out_stream.close();

}

Обратите внимание на то, что при выполнении этой программы производится преобразование символа “\n” в два символа в файле.

Допустимо использование методов open(), rdbuf() из класса ofstream и flush(), put(), seekp(), tellp(), write() из класса ostream.

Буферизованные потоки.

Класс streambuf является основой для потокового буферизованного ввода/вывода. Он используется также для порождения класса файловых буферов (filebuf) и классов istream и ostream, содержащие указатели на streambuf.

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

#include <fstream.h>

#include <fcntl.h>

void main()

{

       char ch;

       long linecount=0;

       filebuf inbuf,outbuf;

       inbuf.open(“infile.dat”,_O_RDONLY | _O_TEXT);

       if(inbuf.is_open()==0)

       {

             cerr<<”cant open input file”;

             return;

       }

       istream is(&inbuf);

       outbuf.open(“outfile.dat”,_O_WDONLY | _O_TEXT);

       if(outbuf.is_open()==0)

       {

             cerr<<”cant open output file”;

             return;

       }

       ostream os(&outbuf);

       while(is)

       {

             is.get(ch);

os.put(ch);

if(ch==’\n’)linecount++;

}

inbuf.close();

outbuf.close();

cout<<”\nYou had: “<<linecount<<” lines”;

}

Допустимо использовать следующие методы класса filebuf: attach(), close(), fd(), is_open(), open(), overflow(), seekoff(), setbuf(), sync(), underflow().

 

Строковые потоки.

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

Класс strsterambuf порождается из класса streambuf. Представители этого класса могут использовать следующие методы: sgets(),sgetn(), sputs(), sputn(), snextc(), sbumpc(), stossc(), sputbackc(), out_waiting(), in_avail(),dbp(), seekof(), seekpos(), overflow(), underflow(), setbuf(), sync().

#include <strstrea.h>

void main()

{

       chat c;

strstreambuf mybuf(1024);

mubuf.sputc(‘A’);

c=mybuf.sgetc();

cout<<c;

}

Двоичные файлы.

Двоичные файлы или потоки содержат последовательности байтов; никаких преобразований символов не выполняется. Для указания двоичного режима в конструктор необходимо добавить описатель ios::binary:

#include <fstream.h>

#include <string.h>

void main()

{

       int i=0;

       long ltellp;

char sample[40]=”Sample string\n”;

       ofstream my_out_stream(“outfile.out”,ios::out | ios::binary);

       while(sample[i]!=0)

       {

             my_out_stream.put(sample[i]);

ltellp=my_out_stream.tellp();

cout<<”\ntellp value: “<<ltellp;

i++;

}

my_out_stream.write(sample,strlen(sample));

ltellp.my_out_stream.tellp();

cout<<”\ntellp after write: “<<ltellp;

my_out_stream.close();

}

 


Дата добавления: 2018-02-15; просмотров: 806; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!