Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
практика.docx
Скачиваний:
4
Добавлен:
20.03.2016
Размер:
196.91 Кб
Скачать

4

ОП

Министерство образования и науки Российской Федерации

Федеральное агентство по образованию

Государственное образовательное учреждение высшего профессионального образования

«Санкт-ПетербургскиЙ государственный университет

ИНФОРМАЦИОНЫХ ТЕХНОЛОГИЙ, МЕХАНИКИ И ОПТИКИ»

ФАКУЛЬТЕТ СРЕДНЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ

ОТЧЕТ

ПО ПРАКТИКЕ ПО ПРОГРАММНОМУ ОБЕСПЕЧЕНИЮ

ПО СПЕЦИАЛЬНОСТИ

«ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ВЫЧИСЛИТЕЛЬНОЙТЕХНИКИ И АВТОМАТИЗИРОВАННЫХ СИСТЕМ» (230105)

Руководитель

Халезова З.П.

2011

1. Введение в ооп

1.1 Особенности языка С++. Указатели и массивы.

Язык С++ имеет свою библиотеку ввода\вывода iostream.h

cout<<" C++ horoshijjazik "<<endl; - операция вывода

cin>> i; - операция ввода

Указатель – это переменная, которая содержит адрес некоторого объекта.

С указателями связаны две специальные операции: & и *.

К указателям можно применять операцию присваивания.

Указатели, как и переменные любого другого типа, могут объединяться в массивы.

Каждому из элементов массива можно присвоить адрес.

На функцию, как и на другой объект языка С, можно создать указатель.

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

Динамическим называется массив, размерность которого становится известной в процессе выполнения программы.

В С++ для работы с динамическими объектами применяются специальные операции new и delete. С помощью операции new выделяется память под динамический объект (создаваемый в процессе выполнения программы), а с помощью операции delete созданный объект удаляется из памяти.

Оператор new имеет две формы записи:

[::] new [(список_аргументов)] имя_типа [(инициализирующее_значение)]

[::] new [(список_аргументов)] (имя_типа) [(инициализирующее_значение)]

Оператор delete имеет две формы записи:

[::] deleteпеременная_указатель

[::] delete [] переменная_указатель

Примеры:

a=newA; выделение памяти для одного объекта класса А

b=newA[3]; выделение памяти для массива объектов класса А

c=newfloat; выделение памяти для одного элемента типа float

d=newfloat[4]; выделение памяти для массива элементов типа float

deletea; освобождение памяти, занимаемой одним объектом

delete [] b; освобождение памяти, занимаемой массивом объектов

deletec; освобождение памяти одного элемента типа float

delete [] d; освобождение памяти массива элементов типа float

Язык С++ имеет свою библиотеку ввода\вывода iostream.h

cout<<" C++ horoshijjazik "<<endl; - операция вывода

cin>> i; - операция ввода

Указатель – это переменная, которая содержит адрес некоторого объекта.

С указателями связаны две специальные операции: & и *.

К указателям можно применять операцию присваивания.

Указатели, как и переменные любого другого типа, могут объединяться в массивы.

Каждому из элементов массива можно присвоить адрес.

На функцию, как и на другой объект языка С, можно создать указатель.

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

Динамическим называется массив, размерность которого становится известной в процессе выполнения программы.

В С++ для работы с динамическими объектами применяются специальные операции new и delete. С помощью операции new выделяется память под динамический объект (создаваемый в процессе выполнения программы), а с помощью операции delete созданный объект удаляется из памяти.

Оператор new имеет две формы записи:

[::] new [(список_аргументов)] имя_типа [(инициализирующее_значение)]

[::] new [(список_аргументов)] (имя_типа) [(инициализирующее_значение)]

Оператор delete имеет две формы записи:

[::] deleteпеременная_указатель

[::] delete [] переменная_указатель

Примеры:

a=newA; выделение памяти для одного объекта класса А

b=newA[3]; выделение памяти для массива объектов класса А

c=newfloat; выделение памяти для одного элемента типа float

d=newfloat[4]; выделение памяти для массива элементов типа float

deletea; освобождение памяти, занимаемой одним объектом

delete [] b; освобождение памяти, занимаемой массивом объектов

deletec; освобождение памяти одного элемента типа float

delete [] d; освобождение памяти массива элементов типа float

1.2 Динамические структуры данных

1.2.1 Способы выделения памяти под данные и линейные списки

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

Самый простой способ связать множество элементов – сделать так, чтобы каждый элемент содержал ссылку на следующий. Такой список называется однонаправленным. Если добавить в каждый элемент вторую ссылку – на предыдущий элемент, получится двунаправленный список, если последний элемент связать указателем с первым, то получится кольцевой список.

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

Над списками можно выполнять следующие операции:

  • начальное формирование списка

  • добавление элемента в конец списка

  • чтение элемента с заданным ключом

  • вставка элемента в заданное место списка

  • удаление элемента с заданным ключом

  • упорядочивание списка по ключу

1.2.2. Стеки и очереди

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

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

1.2.3. Бинарные деревья

Бинарное дерево – это динамическая структура данных, состоящая из узлов, каждый из которых содержит, кроме данных, не более двух ссылок на различные бинарные деревья. На каждый узел имеется ровно одна ссылка. Начальный узел называется корнем дерева.

На рисунке 1.1 приведен пример бинарного дерева. Узел, не имеющий поддеревьев, называется листом. Исходящие узлы называются предками, входящие – потомками. Высота дерева определяется количеством уровней, на которых располагаются его узлы.

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

Рис.1.1 Пример бинарного дерева

Дерево является рекурсивной структурой данных, поскольку каждое поддерево также является деревом. Действия с такими структурами изящнее всего описываются с помощью рекурсивных алгоритмов.

Для бинарных деревьев определены операции:

  • включения узла в дерево

  • поиска по дереву

  • обхода дерева

  • удаления узла

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

Текущий указатель для поиска по дереву обозначен pv, указатель на предка pv обозначен prev, переменная pnew используется для выделения памяти под включаемый в дерево узел. Рекурсии удалось избежать, сохранив всего одну переменную(prev) и повторив при включении операторы, определяющие, к какому поддереву присоединяется новый узел.

Рассмотрим подробнее функцию обхода дерева. Вторым параметром в нее передается целая переменная, определяющая, на каком уровне находится узел. Корень находится на уровне 0. Дерево печатается по горизонтали так, что корень находится слева. Перед значением узла для имитации структуры дерева выводится количество пробелов, пропорциональное уровню узла. Если закомментировать цикл печати пробелов, отсортированный по возрастанию массив будет выведен в столбик. Заметьте, что функция обхода дерева длиной всего в несколько строк может напечатать дерево любого размера – ограничением является только размер стека.

Удаление узла из дерева представляет собой не такую простую задачу, поскольку удаляемый узел может быть корневым, содержать две, одну или ни одной ссылки на поддеревья. Чтобы сохранить упорядоченность дерева при удалении узла с двумя ссылками, его заменяют на узел с самым близким к нему ключём.

1.3 Объекты и классы

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

Объекты выделяются в процессе анализа предметной области с использованием идей абстрагирования от несущественного и классификации родственных объектов. Результатом объектно-ориентированного анализа явл. классы объектов.Class|struct|unioinимя_класса {список_компонентов}|-

Вэтом описании:

Одно из ключевых слов Clas, struct или unioin указывает на начало описания класса, определяют используемый по умолчанию статус доступа к компонентам класса, а так же влияет на возможности наследования свойств этого класса.

Имя_класса – идентификатор.

Список_компонетов- перечень объявлений элементов данных и описаний методов класса.

В качестве спецификаторов доступа используются ключевые слова public(общедоступный), private(собственный),protected(защищенный).

1.3.1 Инкапсуляция и классы

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

1.3.2 Наследование свойств

Наследование есть свойство классов порождать своих потомков и наследовать свойства своих родителей. Класс- потомок автоматически наследует от родителя все элементы данных и методы, а так же может содержать новые элементы данных и методы и даже заменять методы родителя или модифицировать их.

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

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

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

Свойство полиморфизма реализуется не только в механизме замещения однотипных методов при наследовании свойств, но и в механизме виртуализации методов, или позднем связывании методов.

1.3.4 Создание и уничтожение объектов

Создание и удаление объектов осуществляется с помощью специальных методов, которые называются конструкторами и деструкторами соответственно.

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

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

1.3.5 Взаимодействие объектов и сообщений

Отношение родства или включения свойств - это только один из многих типов отношений между классами. Отношение родства также подразумевают определенное отношение взаимодействия, ограниченное свойством инкапсуляции.

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

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

Пример №1

#include<iostream.h>

#include<conio.h>

#include<stdio.h>

classkls

{ intsm;

int m[5];

public:

voidinpt(int);

intsumm();

};

voidkls::inpt(int i)

{ cin>>m[i];}

intkls::summ()

{ sm=0;

for(int i=0;i<5;i++) sm+=m[i];

returnsm;

}

void main()

{ kls k1,k2;

int i;

cout<<"Vvodite el-timassivapervogoobjekta : ";

for(i=0;i<5; k1.inpt(i++));

cout<<"Vvodite el-timassivavtorogoobjekta : ";

for(i=0;i<5; k2.inpt(i++));

cout<<"\n Summa el-tov pervogoobjekta(k1)="<<k1.summ();

cout<<"\n Summa el-tov vtorogoobjekta(k2)="<<k2.summ();

}

Пример №2

#include <iostream.h>

#include <conio.h>

#include <stdio.h>

classSravn

{

intx,y;

public:

voidgetx(int xl) {x=xl;}

voidgety(intyl) {y=yl;}

voidsrav();

};

voidSravn :: srav()

{

if (x>y) cout<<"x bolshe y"<<endl;

else if (y>x) cout<<"y bolshe x"<<endl;

elsecout<<"x raven y"<<endl;

}

void main(void){

clrscr();

Sravn q;

intxl,yl;

cout<<"vveditepervoechislo x: ";

cin>>xl;

cout<<"vveditevtoroechislo y: ";

cin>>yl;

q.getx(xl);

q.gety(yl);

q.srav();;

getch();

}

1.4. Конструкторы и Деструкторы

После описания класса можно объявить один или несколько объектов как экземпляр этого класса. Для доступа к компонентам объекта можно использовать два способа указания имени объекта:

  • Непосредственное указание имени объекта.

  • Косвенное задание имени объекта с помощью указателя и операции косвенного выбора (->).

Первый способ:

имя_объекта .имя_класса :: обращение_к_компоненту

Второй способ:

указатель_на_объект_класса ->обращение_к_компоненту_объекта

Пример 1

#include<iostream.h>

#include<conio.h>

class sum

{

ints,x,y,z;

public:

voidgetx(int x1) {x=x1;}

voidgety(int y1) {y=y1;}

void summa();

};

void sum :: summa()

{

s=x+y;

cout<< "\n CYMMA" <<x<< "u" <<y<< "PABHA:" <<s;

}

void main()

{

sum z,*b=&z;

int x2,y2;

cout<< "\n BBEDuTE IIEPBOE CJIArAEMOE:";

cin>> x2;

cout<< "\nBBEDuTE BTOPOE CJLArAEMOE:";

cin>> y2;

z.getx(x2);

z.gety(y2);

b->summa();

getch();}

В языке С++ определены два специальных типа методов – конструкторы и деструкторы. Конструкторы предназначена для инициализации элементов данных объектов. Описание конструктора имеет следующий формат:

имя_конструктора (список_параметров) {тело_конструктора}

Имя конструктора должно совпадать с именем класса. Конструктор может иметь значения параметров по умолчанию, которые задаются в списке параметров.

В вызове конструктора по сравнению с другими методами существуют следующие особенности:

Если конструктор не вызывается явно, то он будет вызван автоматически при создании объекта с использованием значений параметров по умолчанию.

Если конструктор не описан явно, то он генерируется транслятором без участия программиста.

Для передачи значений элементам данных объекта с помощью конструктора существует два формата записи . Первый из них:

Имя_ класса имя_ объекта = имя_ конструктора ( список_ аргументов);

Второй, более короткий, формат вызова конструктора имеет вид:

Имя конструктора имя_ объекта (список_ аргументов);

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

Например конструктор Sum() можно вызвать двумя способами:SumA=Sum(1,2);

И SumA(1,2). В обоих случаях создается объект А, элементы данныхxиyкоторого получают начальные значения 1 и 2 соответственно. Для обсуждаемого примера конструктора можно осуществить без указания первого аргумента, т.е.SumA(,2); В этом случае элементу данныхxбудет присвоено нулевое значение по умолчанию (x2 = 0). В классе могут быть несколько конструкторов. В классе могут быть несколько конструкторов.

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

~ имя_ класса () {операторы_ тела_ деструктора}

Например, для класса Sumописание деструктора выглядит так:

~ Sum() {}

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

Пример2

#include<iostream.h>

#include<conio.h>

struct Pro

{

private:

intx,y,z;

public:

Pro (int,int);

intputsx();

intputsy();

intputsz();

voidproizv();

~Pro();

};

Pro :: Pro(int x1,int y1) { x=x1; y=y1; }

int Pro::putsx() {return x;}

int Pro::putsy() {return y;}

int Pro::putsz() {return z;}

void Pro::proizv() {z=x*y;}

Pro::~Pro() { }

void main()

{

ints,a,b,c,k;

clrscr();

cout<< "\n BBEDUTE a,b,c u k\n";

cin>> a >> b >> c >> k;

Pro D = Pro(a,b);

Pro E(c,k);

Pro F(a,c);

D.proizv();

E.proizv();

F.proizv();

cout<< "\n D.a=" <<D.putsx();

cout<< "\n D.b=" <<D.putsy();

cout<< "\n D.c=" <<D.putsz();

s=D.putsz()+E.putsz()+F.putsz();

cout<< "\n s=" << s;

F.Pro::~Pro();

E.Pro::~Pro();

D.Pro::~Pro();

getch();

}

Выходные данные:

Пример3

#include <conio.h>

#include <stdio.h>

#include <iostream.h>

#include <string.h>

#define n 10

classstroka

{ int m;

charst[20];

public:

stroka(char *st);

~stroka();

void out(char);

intpoisk(char);

};

stroka::stroka (char * s)

{cout<<"Rabotaetkonstruktor"<<endl;

strcpy(st,s);

}

stroka::~stroka(void)

{cout<<"Rabotaetdestruktor" <<endl;}

voidstroka::out(char c)

{cout<<"Simvol "<<c<<" naiden v stroke "<<st<<" "<<m<<" raz"<<endl;

}

intstroka::poisk(char c)

{m=0;

for(int i=0;st[i]!='\0';i++)

if(st[i]==c) m++;

return m;

}

void main()

{

clrscr();

char c;

cout<<"Vveditesimvoldlyapoiska ego v stroke: ";

cin>>c;

strokastr("abcadbsaf");

if (str.poisk(c))

str.out(c);

else cout<<"Bukva "<<c<<" ne naidena"<<endl;

getch();

}

Выходные данные

    1. Указатель на файловую переменную

Указатель файла – это указатель на структуру типа FILE, которая определена в файле STDIO.H. В этой библиотеке также определены следующие функции:

Fopen() – открыть файл

Пример:

If ((f=fopen (“d.dat”, “wt”)))=NULL)

{print(“ошибка открытия файла”); exit();}

Где f – файловая переменная(указатель на файл)

d.dat – файл на диске

wt – для чтения и записи

NULL – нулевой указатель

Fclose() – закрыть файл

Putc() – записать символ в поток

Getc() – прочитать символ из потока

Fseek() – изменить указатель позиции файла на указанное место

Fprintf() – форматная запись в файл

Fscanf() – форматное чтение из файла

Feof() – возвращает значение «истинно», если достигнут конец файла

Ferror() – возвращает значение «ложно», если обнаружена ошибка

Fread() – читает блок данных из потока

Fwrite() – пишет блок данных в поток

Rewind() – устанавливает указатель позиции файла на начало

Remove() – уничтожает файл

Чтобы объявить указатель на файл, используется оператор FILE *fput;

FILE *fopen(char *filename, char *mode);

Где mode – это строка, содержащая режим открываемого файла. Возможные режимы открытия файлов:

R– открыть для чтения

W – создать для записи

A – открыть для добавления в существующий файл

Rb – открыть двоичный файл для чтения

Wb – открыть двоичный файл для записи

Ab – открыть двоичный файл для добавления

R+ - открыть файл для чтения и записи

W+ - создать файл для чтения и записи

A+ - открыть для добавления или создать для чтения и записи

R+b – открыть текстовый файл для чтения и записи

W+b – создать двоичный файл для чтения и записи

A+b – открыть двоичный файл для добавления или создать для чтения и записи

Rt – открыть текстовый файл для чтения

Wt – создать текстовый файл для записи

At – создать текстовый файл для добавления

R+t – открыть текстовый файл для чтения и записи

W+t – создать текстовый файл для чтения и записи

A+t – открыть текстовый файл для добавления или создать для чтения и записи.

Если файл открыт для записи, то существующий файл уничтожается и создается новый файл. При открытии файла требуется, чтобы он существовал. В случае открытия для чтения и записи существующий файл не уничтожается, однако создается, если он не существует.

Чтение и запись в файл необязательно делать последовательно, можно это делать непосредственно доступом к нужному элементу файла посредством функции fseek(), которая устанавливает указатель позиции файла в нужное место.

1.6 Шаблоны функций и классов

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

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

template<class T>

T sqr_it (T x)

{

returnx*x;

}

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

Пример1:

#include<iostream.h>

template<class T>

T sqr_it (T x)

{

return x*x;

};

main(void)

{

int i=10;

float f=1.1;

long l=2345;

long double ld=123.1e+123;

cout<<"int"<<sqr_it(i)<<"\n";

cout<<"float"<<sqr_it(f)<<"\n";

cout<<"long"<<sqr_it(l)<<"\n";

cout<<"long double"<<sqr_it(ld)<<"\n";

return 0;

}

Выходные данные

Пример2:

#include<iostream.h>

#include<conio.h>

template<class T1, class T2>

T1 maximum (T1 x, T2 y)

{

if(x>=y) return x;

return y;

}

main (void)

{

clrscr();

int i=5;

double d= 3.2345;

long l=123456;

long double ld =1.2e123;

cout<<" maximum iz "<<d<<" i "<<i<<" raven "<<maximum(d,i)<<endl;

cout<<" maximum iz "<<i<<" i "<<d<<" raven "<<maximum(i,d)<<endl;

cout<<" maximum iz "<<ld<<" i "<<l<<" raven "<<maximum(ld,l)<<endl;

cout<<" maximum iz "<<i<<" i "<<l<<" raven "<<maximum(i,l)<<endl;

cout<<" maximum iz "<<l<<" i "<<5.5<<" raven "<<maximum(l, 5.5)<<endl;

cout<<" maximum iz "<<5.5<<" i "<<l<<" raven "<<maximum(5.5,l)<<endl;

getch();

return 0;

}

Выходные данные:

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

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

В качестве параметра при задании шаблона класса может использоваться не только тип, но и другие параметры, например целочисленные значения. Только эти параметры должны быть константными выражениями.

Пример 1:

#include<stdio.h>

#include<conio.h>

#include<iostream.h>

template<class T> class Vector

{

T *data;

int size;

public:

Vector (int);

~Vector() {delete [ ] data;}

T&operator[] (int i) {return data [i];}

};

template<class T> Vector<T> :: Vector(int n)

{

data = new T [n];

size = n;

};

int main()

{

Vector<int>x(10);

for (int i=0; i<10; i++)

x[i] = i*i;

Vector<char>c(5);

for (char ic=0; ic<5; ic++)

c[i]=ic+'a';

for (int i=2; i<7; i++)

cout<< x[i] <<" ";

return 0;

}

Пример 2:

#include <iostream.h>

#include <conio.h>

template<class T1, class T2>

T1 sm1(T1 aa,T2 bb)

{return (T1)(aa+bb);

}

template<class T1, class T2>

classcls

{ T1 a;

T2 b;

public:

cls(T1 A,T2 B): a(A),b(B) {}

~cls(){}

T1 sm1()

{return (T1)(a+b);

}

T1 sm2(T1,T2);

};

template<class T1, class T2>

T1 cls<T1,T2>::sm2(T1 aa,T2 bb)

{return (T1)(aa+bb);

}

void main()

{clrscr();

cls<int,int>obj1(3,4);

cls<double,double>obj2(.3,.4);

cout<<" funkciyasummirovaniyakomponentobjekta 1 ="

<<obj1.sm1()<<endl;

cout<<"funkciyasummirovaniyavneshnihdannih (int,int) ="

<<obj1.sm2(4,6)<<endl;

cout<<"visovglobalnojfunkciisummirovaniya (int,int) ="

<<sm1(4,.6)<<endl;

cout<<"funkciyasummirovaniyakomponentobjekta 2 ="

<<obj2.sm1()<<endl;

cout<<"funkcijasummirovaniyavneshnihdannih (double,double) ="

<<obj2.sm2(4.2,.1)<<endl;

getch();

}

3. Стандартная библиотека шаблонов (STL)

Общее понятие о контейнере

Контейнер – это хранилище объектов (как встроенных, так и определенных пользователем типов). Как правило, контейнеры реализуются в виде шаблонов классов. Простейшие виды контейнеров (статические и динамические массивы) встроены непосредственно в язык С++. Кроме того, стандартная библиотека включает в себя реализации таких контейнеров, как вектор (vector), список (list), очередь (deque), ассоциативный массив (map), множество (set) и некоторых других.

Алгоритм – это функция для манипулирования объектами, содержащимися в контейнере.

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

Аллокатор – это объект, отвечающий за распределение памяти для элементов контейнера.

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

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

Контейнерный класс STL

Описание

Контейнеры последовательности

Vector

Динамический массив

Deque

Двунаправленная очередь

List

Двунаправленный линейный список

Ассоциативные контейнеры

Set

Ассоциативный контейнер с уникальными ключами

Multiset

Ассоциативный контейнер, допускающий дублирование ключей

Map

Ассоциативный контейнер для наборов уникальных элементов

multimap

Ассоциативный контейнер для наборов с дублированием элементов

Адаптеры контейнеров

Stack

Стандартный стек

queue

Стандартная очередь

Priority_queue

Очередь с приоритетами

Пример 2:

#include<functional>

#include<deque>

#include<vector>

#include<algorithm>

#include<iostream>

#include<iterator>

using namespace std;

template<class Arg>

class factorial : public unary_function <Arg, Arg>

{

public:

Arg operator()(const Arg& arg)

{

Arg a=1;

for(Arg i=2;i<=arg;i++)

a*=i;

return a;

}

};

int main()

{

int init[7]={1,2,3,4,5,6,7};

deque<int> d(init, init+7);

vector<int> v((size_t)7);

transform(d.begin(), d.end(), v.begin(), factorial<int>());

cout<<"Dano:"<<endl<<"";

copy(d.begin(), d.end(),ostream_iterator<int,char>(cout," "));

cout<<endl<<endl;

cout<<"Result (!)::"<<endl<<"";

copy(v.begin(), v.end(), ostream_iterator<int,char>(cout," "));

return 0;

}

Пример 3:

#include <set>

#include <algorithm>

#include <iostream>

using namespace std;

typedefmultiset<int, less<int>, allocator>set_type;

ostream& operator<<ostream& out, constset_type& s)

{

copy (s.begin(), s.end(),

ostream_iterator<sett_type::value_type,char>(cout,""));

return out

}

int main(void)

{

set_typesi;

int i;

for (int j = 0; j < 2; j++)

{

for (i = 0; i < 10; ++i)

{

si.insert(si.begin(),i);

}

}

cout<<si<<endl;

set_type si2, siResult;

for (i = 0;i < 10; i++)

si2.insert(i+5)

cout<<si2<<endl;

set_union(si.begin(),si.end(),si2.begin(),si2.end(),

inserter(siResult,siResult.begin()));

set_intersection(si.begin(),si.end(),si2.begin(),si2.end(),

inserter(siresult,siResult.begin()));

cout<<"пересечение:"<<endl<<siResult<<endl;

return 0;

}

1.7 Потоки

Поток в С++ это абстрактное понятие, относящееся к переносу информации от источника к приемнику. В языке С++ реализованы 2 иерархии классов, обеспечивающих операции ввода-вывода, базовыми классами которых являются streambuf и ios.

ВС++ используется достаточно гибкий способ выполнения операций ввода-вывода классов с помощью перегрузки операторов << (вывода) и >> (ввода). Операторы, перегружающие эти операции, обычно называют инсертером и экстрактором. Для обеспечения работы с потоками ввода-вывода необходимо включить файл iostream.h содержащий класс iostream. Этот класс является производным от ряда классов, таких как ostream, обеспечивающего вывод данных в поток, и istream – соответственно чтения из потока.

Пример 1:

#include <stdio.h>

#include <conio.h>

#include <iostream.h>

classcls

{ char c;

short i;

public:

cls(char C,short I): c(C), i(I){}

~cls(){}

friendostream&operator<<(ostream&,constclsobj);

friendistream&operator>>(istream&,cls&);

};

ostream&operator<<(ostream&out,constclsobj)

{out<<obj.c<<obj.i<<endl;

return out;

}

istream&operator>>(istream&in,cls&obj)

{

in>>obj.c>>obj.i;

return in;

}

main()

{clrscr();

cls s('a',10),ss(' ',0);

cout<<"abc"<<endl;

cout<<s<<ss<<endl;

cin>>ss;

getch();

return 0;

}

Общая форма функции перегрузки оператора ввода-вывода имеет вид:

istream&operator>>(istream&поток,имя_класса&объект)

ostream&operator<<(ostream&поток,constимя_класса объект)

Потоки istream или ostream имеют связанное с ними состояние. В классе ios, базовом для классов istream и ostream, имеется несколько public функций, позволяющих проверять и устанавливать состояние потока:

inlineintios::bad() const {return state &badbit; }

inlineintios::eof() const {return state &eofbit; }

inlineintios::fail() const {return state & (badbit | failbit); }

inlineintios::good() const {return state == 0; }

Состояние потока фиксируется в элементах перечисления, объявленного в классе ios:

public:

enumio_state { goodbit = 0x00,

eofbit = 0x01,

failbit = 0x02,

badbit = 0x04 };

Кроме отмеченных выше функций в классе ios есть ещё несколько функций, позволяющих прочитать (rdstate) и очистить (clear) состояние потока:

inlineintios::rdstate() const { return state; }

inline void ios::clear(int_i=0){ lock(); state=_i; unlock(); }

Пример 2:

#include <conio.h>

#include <fstream.h>

#include <iostream.h>

main()

{clrscr();

ifstream in ("file");

int state;

charss[10];

while(1)

{ state=in.rdstate();

if(state)

{if(state&ios::badbit)cout<<"oshinkaotkriiyafaila"<<endl;

else if(state&ios::eofbit)cout<<"v file bolshe net dannih"<<endl;

getch();

return 0;

}

else in>>ss;

}}

Особой разновидностью потоков являются строковые потоки, представленные классом strstream:

classstrstream : public iostream

{ public:

strstream();

strstream(char *s, streamsize n,

ios_base::openmode=ios_base::in | ios_base::out);

strstreambuf *rdbuf() const;

void freeze(boolfrz=true);

char *str();

streamsizepcount() const;

};

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

Пример 3:

#include<strstrea.h>

#include<conio.h>

class string

{

char s[80];

public:

string(char *S)

{

for(int i=0;s[i++]=*S++;);

}

void see()

{

cout<<s<<endl;

}

};

void fun(const char *s)

{

strstreamst;

st<<s<<ends;

stringobj(st.str());

st.rdbuf()->freeze(0);

obj.see();

}

main()

{

clrscr();

fun("1234");

}

В языке С++ для организации работы с файлами используются классы потоков ifstream (ввод), ofstream (вывод) и fstream (ввод и вывод) (рис. 7). Перечисленные классы являются производными от istream, ostream и iostream соответственно. Операции ввода-вывода выполняются так же, как и для других потоков, то есть компоненты-функции, операции и манипуляторы могут быть применены и к потокам файлов. Различие состоит в том, как создаются объекты и как они привязываются к требуемым файлам.

ВС++ файл открывается путем стыковки его с соответствующим потоком. Рассмотрим организацию связывания потока с некоторым файлом. Для этого используются конструкторы классов ifstream и ofsream:

ofstream(const char* Name, intnMode= ios::out, intnPot= filebuf::openprot);

ifstream(const char* Name, intnMode= ios::in, intnPot= filebuf::openprot);

Первый аргумент определяет имя файла (единственный обязательный параметр).

Второй аргумент задает режим для открытия файла и представляет битовое ИЛИ ( | ) величин:

ios::app при записи данные добавляются в конец файла, даже если текущая позиция была перед этим изменена функцией ostream::seekp;

ios::ate указатель перемещается в конец файла. Данные записываются в текущую позицию (произвольное место) файла;

ios::in поток создается для ввода, если файл уже существует, то он сохраняется;

ios::out поток создается для вывода (по умолчанию для всех ofstream объектов), если файл уже существует, то он уничтожается;

ios::trunc если файл уже существует, его содержимое уничтожается (длина равна нулю). Этот режим действует по умолчанию, если ios::out установлен, а ios::ate, ios::app или ios:in не установлены;

ios::nocreate если файл не существует, функциональные сбои;

ios::noreplace если файл уже существует, функциональные сбои;

ios::binary ввод-вывод будет выполняться в двоичном виде (по умолчанию текстовой режим).

Возможны следующие комбинации перечисленных выше величин:

ios::out | ios::trunc удаляется существующий файл и (или) создается для записи;

ios::out | ios::app открывается существующий файл для дозаписи в конец файла.;

ios::in | ios::out открывается существующий файл для чтения и записи:;

ios::in | ios::out | ios::trunc существующий файл удаляется и (или) создается для для чтения и записи;

ios::in | ios::out | ios::app открывается существующий файл для чтения и дозаписи в конец файла.

Третий аргумент – данное класса filebuf, используется для установки атрибутов доступа к открываемому файлу.

Возможные значения nProt:

filebuf::sh_compat совместно используют режим;

filebuf::sh_none режим Exclusive: никакое совместное использование;

filebuf::sh_read совместно использующее чтение;

filebuf::sh_write совместно использующее запись.

Для комбинации атрибутов filebuf::sh_read и filebuf::sh_write используется операция логическое ИЛИ ( || ).

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

void open(const char* name, int mode, intprot=fileout::openprot);

Пример 4:

#include <conio.h>

#include <iostream.h>

#include <fstream.h>

#include <string.h>

class string

{ char *st;

int size;

public:

string(char *ST, int SIZE): size(SIZE)

{st=new char[size];

strcpy(st,ST);

}

~string() {delete [] st;}

string(const string &s)

{st=new char[s,size];

strcpy(st,s.st);

}

friendostream&operator<<(ostream&,const string);

friendistream&operator>>(istream&,string &);

};

ostream&operator<<(ostream&out,const string obj)

{out<<obj.st<<endl;

return out;

}

istream&operator>>(istream&in, string &obj)

{in>>obj.st;

return in;

}

main()

{

string s("asgg",10),ss("aaa",10);

int state;

ofstream out("file");

if(!out)

{cout<<"oshibkaotkritiyafaila"<<endl;

return 1;

}

out<<"123"<<endl;

out<<s<<ss<<endl;

ifstream in("file");

while(in>>ss)

{cout<<ss<<endl;}

in.close();

out.close();

return 0;

}

Пример 6:

#include<fstream.h>

#include<stdlib.h>

#include<conio.h>

void error(char *s1,char *s2="")

{

cerr<<s1<<""<<s2<<endl;

exit(1);

}

main()

{

clrscr();

charc,cc;

int n;

fstream f;

streamposp,pp;

f.open("aaa", ios::in|ios::out);

if(!f) error("OLLIU6KA OTKPbITURqpAUJIA","aaa");

f.seekp(0);

while(1)

{

cin>>c;

if(c=='q'|| f.bad()) break;

f.seekg(0,ios::beg);

while(1)

{

if(((cc=f.get())>=c)||(f.eof()))

{

if(f.eof())

{

f.clear(0);

p=f.tellg();

}

else

{

p=f.tellg()-1;

f.seekg(-1,ios::end);

pp=f.tellg();

while(p<=pp)

{

cc=f.get();

f.put(cc);

if(-pp>0) f.seekg(pp);

}

}

f.seekp(p);

f.put(c);

break;

}

}

}

f.close();

return 1;

}

1.8 Создание файла произвольного доступа

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

Наиболее удобными для организации произвольного доступа при вводе-выводе информации являются компоненты-функции istream::read и ostream::write. При этом, так как функция write (read) ожидает первый аргумент типа constсhar* (char* ), то для требуемого приведения типов используется оператор явного преобразования типов:

istream&istream::read(reinterpret_cast<char *>(&s), streamsize n);

ostream&ostream::write(reinterpret_cast<const char *>(&s),

streamsize n);

2. Решение учебной задачи

2.1. Модуль

Прогрессивным подходом в программировании считается использование модулей и блоков. Модуль (UNIT) – программная единица, текст которой компилируется независимо (автономно). Она включает определения констант, типов данных, переменных, процедур и функций, доступных для использования в вызывающих программах.

2.1.1. Структура модуля

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

Заголовок модуля

UNIT

Имя модуля

{$N+}

Глобальные директивы компилятора

Интерфейсная часть

INTERFACE

Подраздел объявления доступных глобальных меток

USES

Используемые при объявлении модули;

LABEL

Заголовок функций;

CONST

Подраздел объявления доступных глобальных констант;

TYPE

Подраздел объявления глобальных типов;

VAR

Подраздел объявления доступных глобальных переменных;

PROCEDURE

Заголовок доступных процедур;

FUNCTION

Начало раздела объявлений;

Реализационная часть

IMPLEMENTATION

Начало раздела реализации;

USES

Используемые при реализации модули;

LABEL

Подраздел объявления скрытых глобальных меток;

CONST

Под раздел объявления скрытых глобальных констант;

TYPE

Под раздел объявления скрытых глобальных типов;

VAR

Подраздел объявления скрытых глобальных переменных;

PROCEDURE

Тела доступных и скрытых процедур;

FUNCTION

Тела доступных и скрытых функций;

Инициализационная часть

BEGIN

Основной блок модуля;

END

      1. Заголовок модуля

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

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

Примечание:

В рамках ИИО ТР 7.0 поставляются следующие стандартные модули System, Strings, Crt ,Printer,Dos, WinDos,Graph,Overlay,Graph3иTurbo3.

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

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