АЛМАТИНСКИЙ ИНСТИТУТ ЭНЕРГЕТИКИ И СВЯЗИ

 

Кафедра электроники и

компьютерных технологий

 

 

 

 

 

 

 

ОБЪЕКТНО – ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ С++

 

Методические указания к выполнению лабораторных работ

(для студентов всех форм обучения специальностей

050704 – Вычислительная техника и программное обеспечение)

 

 

 

 

 

 

 

 

 

 

Алматы 2006

 

СОСТАВИТЕЛЬ: Т.С. Бимагамбетов. Объектно – орентированное программирование на языке С++. Методические указания к выполнению лабораторных работ(для студентов всех форм обучения специальности 50704 – Вычислительная техника и программное обеспечение).-Алматы: АИЭС, 2006. –36 с.

 

 

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

          Ил. 4, библиогр.  – 7 назв.

 

 

 

 

          Рецензент: ст.преподаватель Г.Т. Мусатаева.

 

 

         

 

 

 

Печатается по плану издания Алматинского института энергетики и связи на 2006 г.

 

 

 

 

 

ã Алматинский институт энергетики и связи, 2006 г.

 

Введение

 

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

Целью методических указаний является обучение студентов составлению программы на основе ООП подход при решении инженерных задач на языке С++, а также методике работы при разработке программы.

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

В ходе подготовки студента к лабораторным занятиям необходимо ознакомиться с теорией и методикой выполнения лабораторных работ по данным методических указаний.

 

 

1 Лабораторная работа №1. Операторы языка С++. Массивы

 

Цель   приобретение навыков программирования с операторами языка С++ и работа с массивами.

 

1.1   Теория метода

 

1.1.1 Программирование разветвленных алгоритмов

Условный оператор является средством реализации  разветвления вычислительного процесса. Структура условного оператора имеет следующий вид:

IF <условие>  <оператор1> ELSE  <оператор2>.

Условный оператор работает по следующему алгоритму. Вначале  вычисляется условное выражение <условие>. Если результат есть TRUE  (ИСТИНА), то выполняется <оператор1> , а <оператор2> не выполняется; если результат есть FALSE (ЛОЖЬ), наоборот, <оператор1> пропускается, а выполняется <оператор2>. Если при этом часть условного оператора, начиная со слова  ELSE, отсутствует, то немедленно управление передается оператору, следующему за условным.

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

 

 

Листинг программы:

#include<iostream>

#include<conio>

#include<math.h>

   int main()

{   clrscr(); int x; float y;

   cout<<"Введите x\n";  cin>>x;

   if (x<5 && x>-2) y=5*x*x+6;   else if (x>=5)  y=x*x*x+7;

   cout<<"Значение y="<<y;  cout <<endl;  getch();  return 0;}

 

          1.1.2 Программирование циклических структур алгоритмов

         

На языке С++ в циклических структурах имеются три различных оператора: for, while, do.

          Цикл for.

Цикл for организует выполнение программы фиксированное число раз, которое заранее известно. Оператор цикла с параметром for имеет такую структуру: for(I=0; I<15; I++). Первое называют инициализирующим, второе

– условием проверки, а третье – инкрементирующим. Эти три выражения, как правило, содержат одну переменную, которую называют счетчиком цикла. Она определяется до того, как начнет исполняться тело цикла.

          Под телом цикла понимается та часть кода, которая периодически исполняется в цикле.

          Цикл while.

          Если нам заранее не известно, сколько раз понадобится выполнить цикл, то используем оператор цикла while, его вид: while<условие>.

          При выполнении оператора while вначале вычисляется и проверяется <условие>. Если выражение <условие> имеет значение TRUE, то выполняется <оператор>; после чего вычисление выражения <условие> и его проверка повторяется. Если <условие> имеет значение  FALSE, то оператор  while прекращает свою работу.

          Цикл do.

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

do {тело цикла}, while <условие>.

          Оператор <тело цикла> выполняется хотя бы один раз, после чего вычисляется выражение <условие>; если его значение есть FALSE, операторы <тело цикла> повторяются, в противном случае оператор do завершает свою работу.

 

          Составить программу вычисления суммы с заданной точностью 10-3.

Считать, что требуемая точность достигнута, если очередное слагаемое по модулю будет меньше, чем Е. Вычислить  .

Листинг программы:

#include<iostream>

#include<conio>

#include<math.h>

   int main()

{

   clrscr(); int k; float y,s,x; const float e=0.001;

   cout<<"Введите x\n";  cin>>x;  s=0;k=1;y=0.1;  while(y>e)

{  y=x/(k*k*k+k*sqrt(abs(x)+1));  s=s+y;   k++;}

   cout<<"Значение y="<<y<<endl; cout<<"Значение s="<<s<<endl;

   cout<<"Значение k="<<k<<endl; getch();  return 0;}

 

          1.1.3 Программирование структур данных типа массив

         

Массив – это структура данных, состоящая из фиксированного числа компонентов одного типа. К компонентам массива обеспечен доступ при помощи указания индексов компонентов. Описание типа массива задается следующим образом: тип данных массива, имя и размер массива.

Int age[4]. Здесь Int - тип, age - имя, в квадратных скобках -  размер массива или элементы массива. В данном примере массив имеет 4 элемента.

          Инициализация массива. Когда массив определен, мы можем присвоить его элементам значения. В примере 12 элементам массива присваивается количество дней для каждого месяца.

#include<iostream>

int main()

{        int month, day, total_days;

          int days_per_month [12]={31,28,31.30,31,30,31,31,30,31,30,31};

          cout<<”\n Введите месяц (от 1 до 12):”; cin>>month;

          cout<<”\n Введите день (от 1 до 31):”; cin>>day;          total_days=day;

          for (int i=0; i<month –1 ; i++) total_days+=days_per_month[I];

          cout<<” Общее число дней с начала года:”<<total_days<<endl; return 0;}

Результат :

Введите месяц (от 1 до 12): 3

Введите день   (от 1 до 31) :11

Общее число дней с начала года: 70

             Аналогично описание двумерного массива Int age[4][4], который требует двух индексов.

          Ниже приводится фрагмент программ для ввода и вывода двумерных массивов:

#include<iostream>

#include<conio>

#include<math.h>

   int main()

{  int matr[100][100],k,i,j,n;  cout<<"Введите n=";  cin>>n;  cout<<endl;  k=1;

   for(i=1;i<=n;i++)

{ for(j=1;j<=n;j++)

{ matr[i][j]=k; k++;  cout<<matr[i][j];

}   cout<<endl;}   cout<<endl;   k=1;   for(i=1;i<=n;i++)

{   for(j=1;j<=n;j++) {  matr[i][j]=k; k++;  cout<<matr[i][j];}   cout<<endl;}

getch();   return 0;}

   

Составить программу для поворота массива на 900.

#include<iostream>

#include<conio>

#include<math.h>

int main()

{ int matr[100][100],   mat[100][100],k,i,j,n;   cout<<"Введите  n=";   cin>>n;

   cout<<endl;   k=1;   for(i=1;i<=n;i++)

{ for(j=1;j<=n;j++)

{ matr[i][j]=k; k++;   cout<<matr[i][j];}   cout<<endl;}   cout<<endl;

   k=1;   for(i=1;i<=n;i++) {   for(j=1;j<=n;j++) {   k++;   mat[i][j]=matr[n+1-j][i];

   cout<<mat[i][j];}   cout<<endl;}   getch();   return 0;}

 

1.2   Задания к лабораторной работе

 

1.2.1 Составить программу вычисления функции y=f (x) для заданного значения х.

1.  2. .

       Функция y=f (x) вычисляется с помощью условного оператора.

 

3.    

4.    

 

     1.2.2 Составить блок-схему и программу вычисления выражения для заданных значений натурального числа n и действительного числа х. Значения выражений вычисляются при помощи операторов цикла. При составлении программ необходимо использовать для организации циклов различные операторы цикла. 6 задачу вычислите с некоторой точностью (см. теорию).

 

5.      .                         6.            .

 

      1.2.3 Составить блок-схему алгоритма и программу для решения задачи по обработке массивов.

1. Дан массив А(5). Найти минимальный элемент массива и их количество.

 2.   Дан массив А(10). Упорядочить элементы массива, следующие за ним по убыванию.

3.  Дан массив А(5,5). Поменять местами наибольший элемент главной и побочной диагоналей с элементом, состоящим на пересечении этих диагоналей. 

4. Дан массив А(4,4). Повернуть массив на 1800.

1.3  Контрольные вопросы

1.3.1      Что такое алгоритм? Назовите и поясните его основные свойства.

1.3.2      Назовите основные типы данных языка С++.

1.3.3      Назовите основные операторы языка С++.

1.3.4      Как происходит работа условного оператора IF?

1.3.5      Какое значение может принимать шаг цикла оператора FOR?

1.3.6      Чем отличаются циклические конструкции с предусловием и с постусловием?

1.3.7      Что представляет собой массив как структура данных?

1.3.8      Каким образом задается описание массива, что в нем указывается?

 

2 Лабораторная работа №2. Функция. Указатели. Структуры.

 

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

 

2.1 Теория метода

 

2.1.1 Функция

Функция представляет собой основу, на которой строится любая программа С++. Каждая программа обязательно должна включать главную функцию с именем main(). Функция представляет собой именованное объединение группы операторов. Это объединение может быть вызвано из других частей программы.

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

Тип_функции, имя_функции (формальных_параметров)

Тело_функции.

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

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

          Вызов функции – это имя функции и круглые скобки со список фактических параметров.

          # include<iostream>

          double Func () { тело функции     }  

int main ()    {Func ()      -----------      return 0;           }

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

Пример - # include<iostream>

          void repchar (char ch,int n)

          { for (int I=0; I<n; I++) cout<<ch; cout<<endl; }  

          int main()

          {char chin; int nin; cout<<”Введите символ:”;

cin>>chin; cout<<”Введите число повторений символа:”;

cin>>nin; repchar (chin, nin); return 0;}

Результат:

Введите символ: *

Введите число повторений символов: 20

********************

В этой программе переменные chin и nin функции main () передаются в качестве аргументов в функцию repchar ():

repchar (char ch,int n); // вызов функции.

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

          Int fact (int n)

          {if (n==1) return 1; else return n*fact (n-1); // вызов самой себя}      

Функция fact вызывает сама себя с модифицированными аргументами.

         

2.1.2  Указатели

Адреса и указатели

Каждая переменная в программе – это объект, имеющий имя и значение. По имени можно обратиться к переменной и получить ее значение. С точки зрения машинной реализации имя переменной соответствует адресу того участка памяти, который для нее выделен, а значение переменной - содержимому этого участка памяти.  Загружаясь в память, наша программа занимает некоторое количество этих адресов. Это означает, что каждая переменная и каждая функция нашей программы начинается с какого-либо конкретного адреса. Указатели позволяют получить значения по адресу. Чтобы получить адрес в явном виде,  применяют унарную операцию &. Адреса имеют целочисленные беззнаковые значения, и их можно обрабатывать как целочисленные величины. Имея возможность с помощью операции & определять адрес переменной или другого объекта программы, нужно уметь его сохранять, преобразовывать и передавать. Для этих целей в языке Си++ введены переменные типа указатель. Как и всякие переменные, указатели нужно определять и описывать, для этих целей используется

разделитель “*”. Кроме разделителя, в определениях и описаниях указателей задается тип объектов, на которые ссылаются указатели. Тогда указатель объявляется следующим образом: тип * <имя переменной>.

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

 

 Операции над указателями

          В языке Си++ допустимы следующие операции над указателями: присваивание, получение адреса самого указателя, унарные операции изменения значения указателя, арифметические операции и операции сравнений.

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

          Примеры

char *z; int *k, *i; int x=10; i=&x; k=i;z= NULL;.

          Соотношение между именем, адресом и значением указателя показано на рисунке 4.

 

 

 

 

 

 


Рисунок 4

 

          Унарные операции “++” и “--”. Числовые значения переменных типа указатель меняются по- разному в зависимости от типа данных, с которыми связаны эти переменные. Если указатель связан с типом char, то при выполнении  операции “++” и “--” его числовое значение изменяется на 1(на 1 байт), если int – на 2, если float –3.

          Аддитивные операции. Две переменные типа указатель нельзя суммировать, однако к указателю можно прибавить целую величину. При этом вычисляемое значение зависит от типа объекта, с которым связан указатель. Например, пусть указатель Р имеет значение 2000 и указывает на целое (int). Тогда в результате выполнения оператора Р=Р+3; значение указателя Р будет 2006. Общая формула Р=Р+ n*(количество байт памяти базового типа указателя).

          Операция вычитание. В отличие от операции сложения, операция вычитания применима не только к указателю и целой величине, но и к двум указателям на объекты одного типа. С ее помощью можно находить разность двух указателей и тем самым определить “расстояние” между размещением в памяти двух объектов. При этом “расстояние” вычисляется в единицах, кратных “длине” отдельного элемента данных того типа, к которому отнесен указатель. Например, int x[5], *i, *k, j; int i=&x[0]; k=&x[4]; j=k-i; j принимает значение 4, а не 8, как можно было предположить, исходя из того, что каждый элемент массива x[ ] занимает два байта.

          Другие арифметические операции над указателями запрещены, например, нельзя умножить указатель на число и т.д.

          Указатели можно сравнивать. Применимы все 6 операций: <,>, <=, >=, =, = =  и !=.

          Пример –  Вывод значения по адресу.

          # include <iostream>

          # include <conio>

          void main ()

{ int var1=11; int *ptr; ptr=& var1; // помещаем в ptr адрес переменной var1

          cout<< *ptr; getch ();}

          Результат 11.

Звездочка, стоящая перед именем переменной, как в выражении  *ptr, называется операцией разыменования. Она позволяет получить значения переменной, хранящейся по адресу. Таким образом, выражении  *ptr представляет собой значение переменной, на которую указывает указатель  ptr.

          Указатель на void. Адрес, который помещается в указатель, должен быть одного с ним типа. Нельзя присвоить указателю на int адрес переменной float.    float var1 = 11; int *ptrint=&var1//  так нельзя

Однако есть одно исключение. Это указатель на void и определяется следующим образом: void*ptr; f loat var1 = 11; void *ptrint=&var1//  так можно.

 

          Указатели и массивы

          В языке Си++ существует связь между указателями и массивами, так как имя массива – это адрес памяти, начиная с которого расположен массив, т.е. адрес первого элемента массива. Таким образом, если был объявлен массив int mas[5]; то mas является указателем на массив, точнее, на первый элемент массива. Для того чтобы получить значения 3-го элемента массива mas, можно написать mas[2] или *( mas+2). Результат будет один и тот же. Рассмотрим на примерах указатели и массивы.

//Обычный доступ к элементам массива

# include <iostream>

# include <conio>

void main ()

{int mas[5]={31, 64, 77, 52, 93 }; for (int i=0; i<5; i++)

cout<< mas[i];

getch ();}

Результат: 31  54   77  52  93.

Доступ к элементам массива через указатель

# include <iostream>

# include <conio>

void main ()

{int mas[5]={31, 64, 77, 52, 93 };

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

cout<< *(mas+i));

 getch ();}

Результат: 31  54   77  52  93.

          Указатели – константы и указатели – переменные. Можем ли мы записать *(mas++), т.е. вместо прибавления шага к i использовать операцию увеличения. Сделать так мы не можем, поскольку имя массива- это адрес памяти и является константой, поэтому это указатель константы. Однако мы можем увеличить указатель, который содержит этот адрес. Покажем на примере.

# include <iostream>

# include <conio>

void main ()

{int mas[5]={31, 64, 77, 52, 93 };

int *ptrint; 

ptrint= mas;

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

cout<<*(ptrint++); getch ();}

Результат: 31  54   77  52  93.

          Здесь мы определили указатель на intptrint – и затем присвоили значение адреса массива mas. Теперь мы можем получить доступ к элементам массива, используя выражение *(ptrint++).

 

2.1.3 Структуры

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

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

 

# include<iostream>

struct part

{

int modelnumber;

int partnumber;

 float cost; };

int main()

{part part1={6244, 373, 217. 55}// Инициализация переменной

cout<<”модель”<<part1.         modelnumber;

cout<<”деталь”<<part1. partnumber;

cout<<” стоимость $”<<part1. cost<<endl;

return 0;

}

Результат:  Модель 6244, деталь 373, стоимость $ 217.55

 

2.2 Задания к лабораторной работе

 

      2.2.1 Вычислить площадь треугольника с помощью формулы Герона.

 

, где Р- периметр треугольника.

 

          2.2.2  Рекурсия функции. Вычислить факториал целого числа с помощью рекурсии.

2.2.3  Дано х=10,2. Присвоить значение х в y и увеличить ее значения на 1 с помощью указателя.

2.2.4  Дано х= 0.5. Найти значение х по адресу.

2.2.5  Дана матрица А[4,4]. Вычислить сумму положительных элементов с помощью указателя.

2.2.6  Задана английская система мер (feet-фут, inches-дюйм), реализуйте с    помощью структуры.

2.2.7 Структурные переменные в качестве аргументов. Вычислить площадь   комнаты в м2.

 

2.3  Контрольные вопросы

2.3.1       Определение, вызов функции.

2.3.2       Передача аргументов в функцию.

2.3.3       Рекурсия функции.

2.3.4       С помощью какой операции получаем адрес переменной?

2.3.5       Чем различие и сходства массива и указателя.

2.3.6       Для чего нужны указатели?

2.3.7       Определение структуры.

2.3.8       Вложенные структуры.

3 Лабораторная работа №3. Объекты и классы

 

Цель работы – работа с объектами. Ознакомиться с методами класса.

 

          3.1 Теория метода

         

3.1.1 Объекты

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

          Примеры объектов:

- автомобили при моделировании уличного движения;

- схемы элементов при моделировании цепи электрического тока;

- страны при создании экономической модели;

- самолеты при моделировании диспетчерской системы.

          3.1.2 Классы

Класс является описанием совокупности сходных между собой объектов. Например,  Prince, Sting и Madonna относятся к классу рок музыкантов. Не существует конкретного человека с именем «рок-музыкант», однако люди со своими уникальными именами являются объектами этого класса, если они обладают определенными характеристиками. Объект класса часто называют экземпляром класса.

          Определения класса. Класс- это производный структурированный тип, введенный программистом на основе существующих типов.

Определение класса начинается с одного из ключевых слов – class, struct, union, за которым следует имя класса. Тело класса заключено в

 

 

 

 

 

 

 

 

 

 

 

 

 


                              

 

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

void_имя ().

          Class sum  //определение класса

          {

          private:

          int somedata; // поле класса

          public:

          void setdata (int d) // метод класса, изменяющий значение поля

          {somedata = d;}

          void showdata () // метод класса, отображающий на экран значение поля

          { cout<<”Значение поля равно=”<<somrdata<<endl;} };       

          Метод класса- это функции, входящие в состав класса. Класс sum содержит два метода: setdata () и showdata (). Ключевые слова private- защищают от несанкционированного доступа к функции, расположенных вне класса. Данные доступны только внутри класса. Данные, описанные с ключевым словом public, напротив, доступны за пределами класса.

          Теперь, когда класс определен,  рассмотрим, каким образом его можно использовать функцией main ().

          int main ()

          { sum s1; s1.setdata (1066); s1.showdata (); return 0;}

Результат: на экране 1066.

Первым оператором функции main () является sum s1, который определяет объект s1. Оператор s1.setdata (1066) вызывает методом setdata () объект s1. Метод присваивает setdata объекту s1 значение, равное 1066. Аналогично вызовы функции showdata () отобразят на экране значения полей объекта s1.

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

Для описания объекта класса используется конструкция : имя_класса, имя_объекта.

          Как только класс определен,  можно создавать производные типы:

- Сomplex x1, x2, D; (Три объекта класса Complex);

- Complex *point=&D (Указатель на объект класса Complex);

- Complex dim[8] =x2 (Массив объектов класса Complex);

- Complex &name =x2 (Ссылка на объект класса Complex).

3.1.3 Конструкторы и деструкторы

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

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

Например. Использование конструктора для выделения памяти.

class copy

{private:

char str[]=”Уронили мишку на пол”;

copy(char*s) // конструктор с одним аргументом

{int length=strlen(s); str=new char[length+1]; strcpy(str,s);}

~copy () // деструктор

{delete [] str;}};

Имя конструктора совпадает с именем класса, а имя деструктора совпадает с именем конструктора (а следовательно, и класса ) и предваряющееся символом ~ (символом тильда). Конструктор и деструктор не возвращают значение. В отличие от конструктора деструктор не имеет аргумента. На данном примере, созданные конструктором предложения копируется и уничтожается деструктором. 

  

3.2 Задания к лабораторной работе

 

3.2.1 Разработайте класс Rectang_Parallel (прямоугольный параллеле-пипед ), которой должен содержать закрытие переменных a,b - стороны прямоугольника – основания, H – высота set параллелепипеда. Разработанный класс должен содержать метод set _init() инициализации указанных переменных, а также методы, вычисляющие и возвращающие значения площадей основания get_So(), боковой get_Sb() и полной  get_Sp() поверхностей по формулам:

Sосн=a*b,  Sбок=2*a*H+2*b*H,   Sполн=2*S+Sбок , также методы вывода полученных значений площадей.

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

3.2.3 Фабрика выпускает несколько типов изделия, поэтому для информации необходимы номер модели изделия, номер детали и ее стоимость. Вывести сведения об изделии. 

3.2.4 Даны сведения о студентах группы: ФИО студента, дата и место рождения студента. Вывести список группы по убыванию года рождения студента. 

 

          3.3 Контрольные вопросы

         

          3.3.1 Какие концепции содержит объектно-ориентированное программирование (ООП).

          3.3.2 Для чего нужно ООП? Какие недостатки структурного программирования.

          3.3.3  Дайте определение объекта класса, приведите примеры.

          3.3.4 Что такое методы и поле класса.

          3.3.5 Объясните смысл класса как тип данных.

          3.3.6 В каких случая удобно использовать конструкторы и деструкторы.

 

 

4 Лабораторная работа №4. Наследование. Иерархия наследования классов

 

Цель – изучение одной из концепций объектно-ориентированного программирования -наследование.

 

4.1 Теория метода

 

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

                Определение производного класса. Для определения производного класса между именем нового класса и базовым классом помещается двоеточие, за ним следует ключевое слово public и имя базового класса.

          Class countdn:public counter. Countdn является наследником класса counter. Для доступа из производного класса к компонентам базового класса, имена которых повторно определены в производном, используется операция

« :: », указывающая области видимости.

          Пример - В программе counten, появится новый класс countdn, добавляющий операцию уменьшения к классу counter.

            //counten.cpp

           ##include<iostream>

#include<conio>

class counter  //базовый класс

{protected: int count;//счетчик

public: counter():count(0) // конструктор без аргумента

{ } counter(int c):count(c)

{ } int getcount()// метод чтения

{return count;}// возвращает значение счетчика

counter operator++()// метод увеличения

{return counter (++count);}

};

   class countdn:public counter  // производный класс

{

 public: counter operator--()// уменьшает значение счетчика

{return counter(--count);}};

   void main()

{

countdn c1;// объект с1

cout<<"\n c1="<<c1.getcount();// вывод на печать

   ++c1;++c1;++c1;// увеличиваем с1 в три раза на 1

   cout<<"\n c1="<<c1.getcount();// вывод на печать

   --c1;--c1;// уменьшаем с1 в два раза на 1

  cout<<"\n c1="<<c1.getcount();// вывод на печать

    cout<<endl;  

    getch();

     }  

Результат программы:

С1=0; С1=3 – после увеличения с1; С1=1 – после уменьшения с1;

Вслед за описанием базового класса counter в программе определен новый производный класс countdn. Он включает в себя новый метод

operator--(), который уменьшает счетчик. В то же время countdn наследует все возможности класса counter: конструктор и методы.

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

 

 

 

 

 

 

 

 

 

 

 


   

 

Рисунок 2

Здесь класс B и C является производным класса A , а класс D производным класса С. Процесс может продолжаться. Ее синтаксис описание:      

          class A {};

          class B:public A {};

class C:public A {};

class D:public C {};

          Множественное наследование. Класс может быть производным не только от одного базового класса, а и от многих.  Этот случай называется множественным наследованием. Ниже показан случай, когда класс С является производным двух классов: А и В и ее синтаксис описания.

          class A

{};

          class B

 {};

          class C: public A, public B

 {};

 

 

 


         

 

 

 

 

 

 


Рисунок 3

          Наследование и доступ. Класс определенный при помощи служебного слова class, может быть как базовым, так и производным. В этом случае по умолчанию члены класса являются закрытыми. Класс, определенный при помощи служебного слова  struct, также может быть как базовым, так и производным, и по умолчанию члены класса являются открытыми. Класс, определенный при помощи служебного слова  union, не может быть ни базовым, ни производным по отношению к какому бы то ни было другому классу. Напишем особенности доступа к элементам базового класса в производном класса в зависимости от типа наследования

Таблица 1

Спецификатор

доступа

Доступ из

самого класса

Доступ из

производных классов

Доступ из внешних классов и функций

public

Есть

Есть

Есть

protected

Есть

Есть

Нет

private

Есть

Нет

Нет

 

4.2  Задания к лабораторной работе

 

4.2.1  Вывести на экран ваш факультет, группу, пол, имя, вес и рост. При этом  факультет, группу взять как базовый класс, а пол, вес и рост как производный класс. Начертите блок-схему иерархию классов.

4.2.2 Создайте иерархию наследования классов. Общие атрибуты и

функции, определяющие данную иерархию, подберите сами.

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Рисунок 4

4.2.3 Создайте программу с классом Book, который включает в себя следующие данные – элементы о книгах, хранящихся в библиотеке:

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

В класс входят следующие функции-члены класса: конструктор с параметром для инициализации автора; деструктор; функция инициализации текущего состояния объектов; функция для просмотра текущего состояния объектов.

Производный класс Type имеет следующее элементы: тематика книг; тип литературы (отечественная или зарубежная); переопределенная функция просмотра состояния объектов.

 

4.3 Контрольные вопросы

 

4.3.1  Наследование и его свойства.

           4.3.2  Множественное наследование.

4.3.3  Иерархия наследования классов.

4.3.4  Базовый и производный класс.

4.3.5  Выбор спецификатора доступа.

 

 

5 Лабораторная работа №5. Полиморфизм

 

Цель – изучить один из главных принципов ООП полиморфизма. А также ознакомиться с ее элементами: виртуальные функции, дружественные функции, перегруженные функции, шаблоны функции и классов, перегрузки операции.

 

5.1 Теория метода

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

          В языке С++ полиморфизм  имеет две формы: 1) виртуальные функции, реализуемая через механизм динамического связывания. Динамическое связывание, в отличие от статического, означает, что определение конкретного экземпляра виртуальной функции производится на этапе компиляции, а непосредственно во время выполнения программы;

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

          5.1.1 Виртуальные функции. Виртуальный означает видимый, но не существующий в реальности. Приведем пример, предположим, имеются базовый класс Shape(фигура) и производные классы  circle(круг),  triangle(треугольник) и т.д. В каждом из этих классов есть функция drow(), которая прорисовывает на экране фигуры. Можно для вывода названия любой фигуры вызывать метод drow() базового класса и предоставить возможность программе самой определить динамически, какую из методов производного класса следует использовать. Чтобы предоставить программе такую возможность, следует объявить функцию drow() виртуальной функцией в базовом классе, а затем переопределить ее в каждом производном классе, чтобы она выводила имя соответствующей фигуры. Это делается с помощью ключевого слова virtual. В базовом классе Shape следует объявить прототип такой функции:  virtual void drow(). Этот прототип объявляет, что функция drow() является виртуальной, не принимает никаких аргументов и ничего не возвращает. Чтобы использовать полиморфизм, необходимо чтобы все классы являлись наследниками одного и того же базового класса.  Рассмотрим следующую программу.

          #include<iostream>

          class base  { public: void show ()

          {cout<<”base\t”;}};

          class derv : public base { public: void show ()

{cout<<”derv”;}};

void main ()

{derv s1; base*ptr; ptr=&s1; ptr->show();}

Так какая же из функции show () реально вызывается? В результате выполнения программы base    base . Так как всегда выполняется метод базового класса. Компилятор не смотрят на содержимое указателя ptr, а выбирает тот метод, который удовлетворяет типу указателя (типом является    base). Чтобы получить результаты производного класса, необходимо перед объявлением функции show () в базовом классе поставить ключевое слово virtual, то в результате выполнения программы получим derv.

5.1.2 Абстрактные классы и чистые виртуальные функции. Базовый класс, объекты которого никогда не будут реализованы, называются абстрактными. Единственное их назначение – это возможность другим классом наследовать от них интерфейс и реализацию. Классы, объекты которые могут быть реализованы, называются конкретными классами. Чтобы создать абстрактный класс  (защитить нам базовый класс от использования не по назначению) необходимо в класс ввести чистую виртуальную функцию. Чистой виртуальной функцией является такая функция, у которой в ее объявлении тело определено как 0, virtual viod Show()=0. Как только в базовом классе окажется чистая виртуальная функция, необходимо будет позаботиться о том, чтобы избежать ее употребления во всех производных классах. Если теперь в main() попытаться создать объект абстрактного класса, то компилятор даст синтаксическую ошибку. 

5.1.3  Дружественные функции. Как правило, переменные-члены классов являются закрытыми, а доступ к ним можно получить только посредством методов своего класса. Но существует категория функций, создаваемых специально для того, чтобы преодолеть эти ограничения. Эти функции, называемые дружественными и объявляемые в описании класса с помощью ключевого слова friend, получают доступ к переменным -членам класса, сами не будучи его членами. Одна функция может быть дружественной сразу нескольким классами. Дружественные функции как мост между классами. Приведем пример.

#include<iostream>

class beta;

class alpha {private: int data; public:

alpha () : data(3) {}

friend int frifunc(alpha. beta);};

class beta {private: int data; public: brta () : data(7) {}

friend int frifunc(alpha. beta);};

int frifunc(alpha a. beta b) {return (a.data+b.data)};

void main() {alpha aa; beta bb; cout<< frifunc(aa, bb)}

 

Чтобы функция frifunc() имела доступ и к тем, и к другим скрытым данным, мы делаем ее дружественной функцией. Объявление функции frifunc() в классе alpha ссылается на класс beta , а значит, beta должен быть объявлен до alpha.

                5.1.4 Перегруженные функции. Перегруженная функция выполняет различные действия, зависящие от различного числа аргументов и от типов данных, передаваемых ей в качестве аргументов.

          #include<iostream>

          void repchar(); void repchar(char);

void repchar (char,int); // прототипы

void main ()

{repchar(); repchar(‘=’);  repchar(‘+’,15);}

void repchar() {for(int I=0; I<5; I++) cout<<’*’;}

          void repchar(char ch)

{ for(int I=0; I<10; I++) cout<<ch;}

          void repchar (char ch,int n)

{for(int I=0; I<n; I++)  cout<<ch;}

Результат: 

*****

          ==========

++++++++++++++

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

          5.1.5 Шаблоны функции и классов. Перегруженные функции обычно используются для выполнения одинаковых операций над различными типами данных. Если операции идентичны для каждого типа, то их можно выполнить более компактно и удобно, используя шаблоны функции. Можно написать единственное определение шаблона функции, а затем, основываясь на типах аргументов, указанных в вызовах этой функции, С++ автоматически будет генерировать разные функции для соответствующей обработки каждого типа. Таким образом, определение единственного шаблона определяет целое семейство решений.

          Все определения шаблона функции начинаются с ключевого слова template (шаблон), за которым следует список формальных типов параметров функции, заключенных в угловых скобках <>.Каждый формальный тип параметра претворятся ключевым словом class. Например, напишем функцию вычисления модуля чисел, для разных типов n(без шаблона)

int absul(int n)  {return (n<0)? –n:n;}   absul(duoble n) {return (n<0)? –n:n;}

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

#include<iostream>

          template <class T>

          T absul(T n) { return (n<0)? –n:n;}

          void main () {int intl=-5; double doub=7;

cout <<absul(intl); cout<< absul(doub);}  

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

          #include<iostream>

          template <class Type>

          class sm

{ private:

Type n;

public:

void getdata(Type d)

{n=d;}  

          void showdata ()

{cout<<d<<endl;}

};

          void main()

 {

 sm<int> s1;

 sm<float> s2;

 s1.getdata(15);

 s1.showdata();

 s2.getdata(111.56);

 s2.showdata();

 }

Здесь sm является шаблонным классом. Шаблонный аргумент  Type используется вместо фиксированного типа sm<int> s1 создает объект s1.

          5.1.6 Перегрузка операции.

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

встроенных операции, однако при помощи перегрузки операций можно расширить возможности С++. Мы уже пользовались перегрузкой операторов <<  и >> . Операция перегружается путем составления функции которой имя функции состоит из ключевого слова operator, после которого записывается перегружаемая операция: тип operator операция (список_параметров ) {тела функции}.

          Цель перегрузки – обеспечить выражения для типов, определенных пользователем. Определяя перегрузку операции, необходимо сохранять стандартный формат его применения. Кроме того, перегружать только существующие операторы. Список некоторых операции, перегрузка которых допустима С++ , +, -, *, /, %, -<, --, =, <>, ++, new, delete .

На перегрузку операции накладывают следующие ограничения:

а) невозможно изменить приоритет операции;

б) невозможно изменить синтаксис операции;

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

г) невозможно создавать новые операции;

д) запрещается переопределять такие операции: .(доступа к члену), * (указателя на член), (расширения области видимости), ?:(условного выражения).

          Для примера, рассмотрим перегрузку унарных операции. Пример со счетчиком.  

#include<iostream>

class counter

{

private:

int count;

public:

couinter():count(0)

{}

void getcount()

{return count++;}

void main

{counter c1;

cout<<c1.getcount();

}

Здесь объект этого класса, мы увеличиваем вызовом метод: c1.getcount(). Используя перегрузку унарных операции увеличиваем ++ можно писать ++с1, тогда листинг программы

……………….    

void getcount ()

{return count;}

void operator++()

{++count;}

void main

{counter c1; ++c1;

cout<< c1.getcount(); }

Результат будет одинаковым. В этой программе использовано ключевое слово operator для перегрузки операции ++. Сначала пишут тип, затем саму операцию (++) и список аргументов. Такая запись говорит компилятору о том, что если операнд принадлежит классу   counter, то нужно вызывать функцию с таким именем, встретив в тексте программы ++. В данном примере объектом класса counter ++с1, вызывают функцию operator++().

5.2  Задания к лабораторной работе

 

5.2.1 Создайте программу с классом Computer, который включает в себя следующие элементы-данные о компьютерах, которые можно приобрести: тип компьютера (портативный, настольный), производитель, тип процессора, частота процессора, емкость ОЗУ, емкость винчестера.

В базовый класс входят следующие функции- членов: конструктор по

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

Производный класс имеет следующие элементы: размер монитора, производитель монитора, преопределенную функцию вывода полной информации о компьютере.

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

5.2.3 Шаблоны и классы. На примере 5.1.5 использованы шаблоны функции с одним аргументом функции. Н этой основе создайте шаблоны функции с тремя аргументами, два из которых шаблонные, а один базового типа. Шаблоны функции предназначены для поиска в массиве заданного числа. Она возвращает индекс найденного значения или –1 в случае его отсутствия в массиве. Аргументами является указатель на массив, значение которого нужно искать, а также размер массива.

5.2.4 Создайте два класса alpha и beta. В классе alpha, beta дружественный класс. Все скрытые данные переменных alpha вывести с помощью класса beta.

5.2.5 Используя перегрузки операции «+» сложите две переменные (длину feet  и inches, 1 feet=12 inches).

5.2.6 Используя перегрузку операции «+»объедините две строки.

         

5.3 Контрольные вопросы

.

5.3.1 Полиморфизм.

5.3.2 В каких случаях не следует делать методы класса виртуальным.

5.3.3 Зачем в методе класса используют значения параметров, заданные по умолчанию, если можно его перегрузить?

5.3.4 Шаблоны методы могут быть виртуальными?

5.3.5 Чем отличается дружественная функция от дружественного класса.

5.3.6 Перегрузка унарных и бинарных операций.

5.3.7 Множественная перегрузка.

 

6 Лабораторная работа №6. Потоковые классы и файла.

 

Цель работы – ознакомление с потоковыми классами и создание файлов.

 

          6.1 Теория метода

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

Классификация потоков. По направлению обмена потоки можно разделить на входные  (данные вводятся в оперативную память ), выходные и двунаправленные (допускается как извлечение, так и включение).

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

Иерархия потоковых классов. Для поддержки потоков библиотека языка С++ содержит иерархию классов, построенную на основе двух базовых классов ios  и streambuf . Класс  ios содержит общие для ввода-вывода поля и методы, класс  streambuf обеспечивает буферизацию потоков и их взаимодействие с физическими устройствами. От этих классов наследуется класс istream для входных потоков и класс ostream – для выходных. Два последних класса являются базовыми для класса iostream, реализующие двунаправленные потоки. Далее в иерархии классов располагаются файловые и строковые потоки. 

6.1.2 Стандартные потоки. Язык С++ поддерживает четыре предопределенных объектов, выполняющих ввод -вывод. Объект cin –стандартный ввод и является объектом класса istream, в котором определена перегруженная операция “>>”. Объект  cout –стандартный вывод и является объектом класса ostream, в котором определена перегруженная операция “<<”. Объекты cerr (не буферизированными) и clog (буферизированный) –стандартный вывод ошибки и является объектом класса ostream. Эти стандартные объекты в заголовочном файле <oistream>. Напомним, что операции “>>”  и  “<<” бинарные, у них левым операндом является объект –поток, а правым –объект, которой требуется извлечь или поместить в этот поток. Например, оператор cout<<х посылает значение переменной х в объект  cout класса ostream для вывода. Перегруженный метод operator << возвращает ссылку на объект ostream, для которого он был вызван. Поэтому несколько операций вывода можно объединить в цепочку cout<<”x=”<<x<<endl; 

6.1.3 Файловый ввод-вывод. Стандартная библиотека языка С++ содержит три класса для работы с файлами: ifstream – класс входных файловых потоков, ofstream – класс выходных файловых потоков, fstream – класс двунаправленных файловых потоков.

Режимы работы файлов.

Режимы открытия файлов: ios::in – открыть файл для чтения;  ios::app – открыть файл для записи только в конец файла;  ois::out -  открыть файл для записи;  ios::ate – указатель потока переместится в конец исходного открытого файла;  ios::trunc – удалить старое содержимое файла;  ios::binary – Открыть файл в двоичном режиме.

Обращение к файлам. ifstream filename (“name.txt”, ios::in) – открывает файл для чтения; fstream filename (“name.txt”, ios::in|ios::out) – открывает файл для чтения и записи; filename.close() – закрывает файл.

Путь к файлам – fstream (“d:\\name.txt”, ios:in|ios::out); определение признака конца файла – eof, while(!filename.eof()). Проверка ошибки исполнения файла – if (!filename) {cout<<”file error”; exit(1);}.

Приведем примеры.

Посимвольный запись в файл.

#include<fstream>

#include<iostream>

#include<string>

void main()

{string str=”Время - великий учитель, но увы, он убивает своих учеников”;

ofstream outfile (“d:\\test.txt”, ios::out);

if (!outfile) {cout<<”file error”; exit(1);}.

for(int i=0; i<str.size();i++) outfile.put(str[i]);}

Считывания из файла.

#include<fstream>

#include<iostream>

void main()

{char ch;

 ifstream infile (“d:\\test.txt”, ios::in);

while (!infile.eof())

 {infile.get(ch); cout<<ch;}}

6.1.4 Двоичный ввод-вывод. Форматированный ввод –вывод чисел целесообразно использовать при их небольшой величине и малом количестве. В противном случае, гораздо эффективнее использовать двоичный ввод –вывод, при котором числа хранятся таким же образом, как в оперативной памяти компьютера, а не в виде символьных строк. Например, значения типа float занимает 4 байта, а в то время форматированная версия 6.02314е13 занимает 10 байтов. Приведем на примере объектный ввод –вывод данных.

Ввод данных.

#include<fstream>

#include<iostream>

class person

{protected:

char name[80]; short age;

public:

void getdata() {cout<<”Введите имя:”; cin>>name;

cout<<”Введите возраст::”; cin>> age ;}};

void main

{person pers; pers getdata();

ofstream outfile (“person.dat”, ios::binary);

outfile.write(reinterpret_cast<char*>(&pers), sizeof(pers));}

 Вывод данных.

 #include<fstream>

#include<iostream>

class person

{protected:

char name[80]; short age;

public:

void showdata() {cout<<”Имя:”<<name<<endl;

cout<<”Возраст:”<< age ;}};

void main

{person pers;

ifstream infile (“person.dat”, ios::binary);

infile.read(reinterpret_cast<char*>(&pers), sizeof(pers));pers.showdata();}

В этом примере используются две функции   write()  (метод класса ofstream) и read (метод класса ifstream), они переносят байты из буфера в файл и обратно. Параметрами этих функций являются адрес буфера и его длина. Адрес вычисляется с использованием оператора  reinterpret_cast относительно тип char*, т.е. изменяет тип данных в определенной области памяти. Вторым параметром является длина в байтах sizeof (pers).

6.1.5 Файлы произвольного доступа. Для последовательного поиска данных обычно начинается чтение с начала файла и читает всю информацию до сих пор, пока не будут найдены требуемые данные. Эти действия никаких дополнительных манипуляций с указателями (текущая позиция) файлов проводить не требуют. Но бывает необходимость читать и писать с произвольного места файла. Классы   istream и ostream содержат такие функции seekg() и tellg(), который позволяет устанавливать и проверять текущий указатель чтения, а seekp() и tellp() –указатель записи. Функция seekp() может использоваться в двух вариантах. Первый -с одним аргументом 0, указывающий начало позиции файла. Второй -функция с двумя аргументами, первый указывает сдвиг от определенной позиции в файле, а второй определяет позицию с которой отсчитывается этот сдвиг. Второй аргумент имеет три значения: beg – начала файла, cur –текущая позиция указателя, end – конец файла. Например seekp(-10, ios::end) –установить указатель записи на 10 байтов до конца файла.

#include<fstream>

#include<iostream>

class person

{protected:

char name[80]; short age;

public:

void getdata() {cout<<”Введите имя:”; cin>>name;

cout<<”Введите возраст::”; cin>> age ;}

void showdata() {cout<<”Имя:”<<name<<endl;

cout<<”Возраст:”<< age ;}};

void main

{person pers;

ifstream infile;

infile.open(“person.dat”, ios::in|ios::binary);

infile.seekg(0,ios::end);

int endposition=infile.tellg();

int n=endposition/sizeof(person);

cout<<”\nВ файле”<<n<<”человек”;

cout<<”\n Введите номер персона:”; cin>>n;

int position=(n-1)*sizeof(person); infile.seekg(position);

infile.read(reinterpret_cast<char*>(&pers), sizeof(pers));pers.showdata();

cout<<endl;}

 

6.2 Задания к лабораторной работе

 

6.2.1 Напишите программу, использующую стандартные объекты cin, cout, cerr и clog класса iostream.

6.2.2 Даны следующие объекты int i=2; float f=1.5; double d=12.21. Напишите программы, обеспечивающие ввод из файла test.inp значений указанных ранее объектов. Используйте при вводе файловый объект и перегруженную операцию “>>”. Просмотрите проверку достижения конца файла и обработку ошибки ввода. Подключите необходимые заголовочные файлы.

6.2.3 В файле дана матрица размером 4х4. Напишите программу, которая вычисляет сумму элементов строки.

6.2.4 Напишите программу, которая позволяет найти в телефонном справочнике нужные сведения. Программа должна запрашивать фамилию человека и выводить его телефон. Если в справочнике есть люди с одинаковыми фамилиями, то программа должна вывести список этих людей. Например, Введите фамилию ->Петров.

                   В   справочнике данных о Петрове нет. 

                   -> Иванов

                   Иванов Вася 57-12-45

                   Иванов Сергей 24-34-82

          6.3 Контрольные вопросы

          6.3.1 Потоки. Виды потоков. Иерархия потоковых классов.

          6.3.2 Что такое перегруженные операции “>>” и ”<<” ?

          6.3.3 Какое отличие между cerr и clog?

          6.3.4 Файловые потоки. Какие классы содержат файловые потоки.

          6.3.5 Какой обязательный параметр принимает конструктор объекта ofstream?

6.3.6      Файлы последовательного и произвольного доступа.

 

 

7 Лабораторная работа №7. Многофайловые программы. Проекты

 

Цель работы – работа с многофайловыми программами и создание проекта.

 

          7.1 Теория метода

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

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

7.1.2 Создание многофайловой программы. Допустим имеется библиотечный файл theirs.obj. К нему, соответственно, прилагается заголовочный файл theirs.h. Написанный вами на файл mine.cpp вставляется заголовочный файл theirs.h с помощью директивы #include:  #includetheirs.h”. Кавычки вместо угловых скобок заставляют компилятора искать файл в текущем каталоге. Компилятор создает объектные файлы и затем компоновка exe файл. Это называется процессом сборки. Убедитесь, что все компоненты theirs.h , theirs.obj,  mine.cpp –должны находится в одном каталоге. Большинство компиляторов управляют многофайловыми приложениями, называя их проектами. В проект входят все файлы, необходимые в программе. В нем также содержится инструкции и по их компоновке называемый файлом проект. Расширение этого файла зависит от конкретной среды

программирования и от ее производителя. Одно из замечательных свойств проектов –они хранят данные о том, когда был откомпилирован каждый исходный файл. Перекомпилируются только те файлы, которые были изменены после последней сборки. Структура многофайловых приложений показана на рисунке 5.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

          Межфайловые переменные. Для того, чтобы глобальные переменные были видны из другой программы, необходимо использовать зарезервированное слово extern. Например, // Файл А  int globalvar; // Файл B

Globalvar=3; // Так нельзя.

Для этого в файле В пишем extern. // Файл B extern int globalvar;  globalvar =3    // Так можно.

          Межфайловые функции. Объявления функции задает ее имя, тип возвращаемых данных и типы всех аргументов, а в определение включается еще тело функции. Поэтому можно определить функции в одном файле и вызывать ее в другом. Нужно только объявить функцию в том файле, откуда будет производиться вызов. Например,  // Файл А  int add(int a, int b){return a+b}; // Файл B int add(int , int ); // объявления функции. int answer=add(3,2);

          Межфайловые классы. Классы в межфайловых отношениях не так, как переменные и функций. Так как в последних двух определены типы , определение класса не подразумевает резервирования памяти. Поэтому, чтобы иметь доступ к классу из любого файла, входящего в программу, необходимо определить класс в каждом файле. Определение класса в файле А и объявление его в файле В не означает, что компилятор сможет создать в файле В объекта данного класса. Эти проблемы решаются с помощью заголовочных файлов. Использования с заголовочных файлов дает возможность вставить одну и ту же информацию в разные файлы. В них содержатся объявления переменных или функций, которые и включается в исходные тесты программы. Тем самым можно открыть доступ к этим переменным и функциям из множеств файлов. Однако, каждый элемент программы должен быть определен в каком-то месте. Например, определения класса и ее переменная и функция объявлены в fileh.h, определение метода класса в   fileh.cpp, создание объекта и вывод значения на экран в test.cpp.

          //fileh.h

          #include <iostream>

          #include <conio>

          class someclass {public: int memvar;  int memfunc(int, int);};

 

//fileh.cpp

#include “fileh.h”

int someclass::memfunc(int a, int b)

{memvar=a*b; return memvar; }

 

//test.cpp

#include “fileh.cpp”

void main ()

{someclass obj1; int var1=obj1.memfunc(5,3); cout<<var1; getsh();}

Результат: 15.

7.1.3 Создание проекта. Для создания проекта в VC++ 6 версии необходимо сделать следующее. Разместить эти файлы в одной папке, например, С:\C++\Class, а затем создайте пустой консольной проект. Для этого выполните File->New, щелкните на вкладке  Projects, выберите  Win32 Console Application, укажите, где создать проект    С:\C++\Class, дайте ему имя  Time, нажмите  Ok Finish, Ok. Теперь добавьте в проект эти три файла: Project->Add

To Project->Files, найдите файл fileh.h в папке Class, нажмите Ok. Аналогично добавьте в проект файлы  test.cpp и fileh.cpp. Рассмотрим закладку  ClassView рабочего пространства   Workspace: откройте + рядом с классом  Time и рассмотрите обозначения элементов и методов класса. Затем раскройте все +на вкладке  FileView и посмотрите , как файлы были добавлены в проект. Откомпилируйте и скомпонуйте проект, а затем нажмите  ! для выполнения результирующего файла  test.cpp.

 

7.2 Задания к лабораторной работе

 

7.2.1 Двухфайловые взаимодействия. Даны список товаров, имеющихся на складе, включает: наименование товаров, оптовую цену единицы  товара и дату поступления товара на склад. В одном файле goods.cpp создать класс   именем Class goods, который запрашивает от пользователя и выводит на экран эти данные, а также розничную цену, при этом используйте static float percent (cout<<price*(goods::percent;). Во втором файле (главном функции main()) вывести розничную цену соотношением 1.5 раз больше чем оптовая (float goods::percent=1.5;), где   price – оптовая цена товара.

7.2.2 Трехфайловые взаимодействия. В первом файле (fileh.h) создать класс, где задаются переменные данных и функция с аргументами. Во втором файле (fileh.cpp) вычислить значения функции y=5x2  значения х задавалось в первом файле. В третьем файле (test.cpp (главная функция main())) вывести на экран значения   y с помощью объекта класса.

7.2.3 Создать проект, содержащий несколько связанных между собой программы. В первой программе будет объявлен класс baking (выпечка), которой содержит перечень продуктов (данных класса) и методы класса (данных функции) -это рецепты приготовления различной выпечки из этих продуктов. Во втором -опешите класс в заголовочном файле, создайте файл определений методов класса и итоговую программу класса baking, где при наличии 200 гр. масла, 3 стаканов муки, 2 яиц, 1 стакана сахара  выдавались бы рецепты, использующие указанное количество этих продуктов. Создайте консольный проект для решения этой задачи.

 

7.3 Контрольные вопросы

 

          7.3.1 Что представляют собой межфайловые программы.

          7.3.2 Опишите структуру многофайловых приложений.

          7.3.3 Почему определение класса необходимо помещать во всех файлах?

          7.3.4 Как создать проект?

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

 

 

Список литературы

 

1. Фридман А.Л.Язык программирования С++. Курс лекций.

- Москва: Интернет-Университет информационных технологий, 2003.

2. Лафоре Р. Объектно – ориентированное программирование в С++. 

- Санкт-Петербург: Питер, 2003.

3. Подбельский В.В. Язык СИ++: Учебное пособие. –М.: Финансы и статистика, 2001.

4. Культин Н. С/С++ в задачах и примерах. – Санкт-Петербург: БХВ-Петербург, 2004.

5. Шиманович Е. С/С++ в примерах и задачах: Учебное пособие. –Мн.: Новое знание, 2004.

6. Давыдов В.Г. Технология программирования С++. Учебное пособие. – СПб.: БХВ -Петербург. 2005.

7. Бимагамбетов Т.С. Программирование на языке Си: Учебное пособие. Алматы: АИЭС, 2005.

 

 

Содержание

 

Введение……………………..……………………….………………..3

1 Лабораторная работа №1. Операторы языка С++. Массивы…..…4

2 Лабораторная работа №2. Функция. Указатели. Структуры.…….8

3 Лабораторная работа №3. Объекты и классы…………………….14

4 Лабораторная работа №4. Наследование. Иерархия

   наследования классов………………… ……………………….…..17

5 Лабораторная работа №5. Полиморфизм….……………………...21
6 Лабораторная работа №6. Потоковые классы и файлы…….……27

7 Лабораторная работа №7  Многофайловые программы.

Проекты………………………………………………………………..31

Список литературы…….……………………………………..….……35

 

 

 

 

 

 

 

 

 

 

Доп. план 2006 г., поз 68

 

 

 

Бимагамбетов Толеугали Сапарович

 

 

 

 

 

ОБЪЕКТНО – ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ

НА ЯЗЫКЕ С++

 

Методические указания к выполнению лабораторных работ

(для студентов всех форм обучения специальности

050704 – Вычислительная техника и программное обеспечение)

 

 

           

 

Редактор        Т.С. Курманбаева

 

 

 

 

 

Подписан в печать ____________                    Формат 60х84    12/16

Тираж 200 экз                                                     Бумага типографская №1

Объем 2.3 уч.-изд.л.                                           Заказ _____. Цена 230 т.

 

 

 

 

Копировально-множительное бюро

Алматинского института энергетики и связи

050013 Алматы, Байтурсынова 126