48531

Основы языка C++

Конспект

Информатика, кибернетика и программирование

Основной код программы начинается в строке 3. при вызове основной функции. Функция —это блок программы, который выполняет одно или несколько действий. Обычно функции вызываются другими функциями, но функция main() вызывается автоматически при запуске программы, каждая программа должна содержать эту функцию, причём только одну, нельзя назвать другую функцию так же.

Русский

2013-12-17

790.86 KB

7 чел.

8

Содержание

Лекция № 1. Основные понятия языка C++. Переменные, константы. Резервирование памяти, инициализация, типы и их значения, символы. Выражения. Операторы. операции. Линейные программы. Операторы отношений. логические операторы. оператор ветвления. 3

Процесс создания программы. 3

Переменные и константы. 4

Оператор ветвления if. 5

Лекция № 2. Циклы. Счётчики. Указатели. память. размещение объектов в области динамической памяти. ссылки. Адреса. передача аргументов функции как ссылок. возвращение нескольких значений. 7

Циклы. 7

Оператор switch. 8

Указатели. 9

Лекция № 3. Функции. Объявление и определение функции. выполнение функции. Локальные и глобальные переменные. Аргументы функции —фактические и формальные. Перегрузка функций. возвращаемые значения. Массивы. 11

Функции. 11

Использование функций. 11

Макрофункции 12

Перегрузка функций. 13

Возврат более одного результата. 13

Лекция № 4. Массивы. Элементы массива. инициализация массива. объявление массива. Динамические и статические массивы. Многомерные массивы. Строковые. 15

Массивы. 15

Лекция № 5. Структуры. Доступ к элементам структуры. Инициализация. Выделение памяти. работа с динамическими типами данных . работа со  стеком, очередью, деком, деревом. (Самостоятельное изучение) 17

Создание новых типов 17

Шаблон структуры 17

Структурные переменные 17

Инициализация структуры 18

Массив структур 19

Передача структуры в функцию 20

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

Лекция № 6. Базовые классы. Классы и члены. Получение доступа к членам класса. Ограничение доступа к членам класса. определение методов класса. Конструкторы и деструкторы. Вложенные классы. структуры. 22

Классы. 22

Доступ к членам класса. 22

Лекция № 7. Перегрузка функций-членов. Инициализация объектов. Перегрузка операторов.  Операторы преобразований. Наследование. 24

Дополнительные возможности использования функций. 24

Функции  —друзья. 25

Перегрузка операторов. 25

Конструктор-копировщик. 26

Лекция № 8. Наследование   Закрытый. Защищённый Форматированный ввод. Ввод-вывод в файл. 28

Наследование. 28

Инициализация наследуемых классов. 28

Потоки. 29

Управление выводом данных: 30

Лекция № 9. Использование файлов для ввода-вывола. 31

Использование файлов для ввода- вывода информации. 31

Строки. 32

Лекция № 10. . Виртуальные методы. Полиморфизм. 33

Виртуальные методы. 33

Как работают виртуальные функции. 33

Абстрактные классы. 35

Лекция № 11. Специальные классы и функции. Вложение. Делегирование. Классы друзья. Функции -друзья. Связные списки. 36

Статические переменные-члены. 36


. История развития языков Концепции структурного и объектно-ориентированного программирования. особенности написания программ для системы
Windows, концептуальные отличия .Составные части программы на С++. Использование функций

Основные понятия языка C++. Переменные, константы. Резервирование памяти, инициализация, типы и их значения, символы. Выражения. Операторы. операции. Линейные программы. Операторы отношений. логические операторы. оператор ветвления.

  1.  Процесс создания программы.

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

  1.  Написать исходный файл с расширением .cpp.
  2.  скомпоновать исходный файл и получить объектный файл с расширением .obj.
  3.  скомпоновать предыдущий файл. С необходимыми библиотеками и получить исполняемый файл .exe.

Традиционно первая программа позволяет вывести на экран сообщение, что «родился» новый программист.

#include <iostream.h>

int main()

{

cout <<”Привет Мир! Меня зовут …\n”;

return 0

}

  1.  Рассмотрите следующую программу и попытайтесь понять, что она делает, не запуская её на выполнение.

#include <iostream.h>

int main()

{ int x=5;

int y=7;

cout <<”\n”

cout << x+y <<”  ”<<x*y;

cout <<”\n”

return 0

}

2 какие ошибки будут при компиляции, почему?

#include <iostream.h>

int main()

{  

cout <<”Привет Мир! Меня зовут …\n

return 0

}

В стороке 1 подключается файл .. в текущий. Сигнал # служит для препроцессора. С этими строками препроцессор работает до того как начнётся компиляция программы.

Основной код программы начинается в строке 3. при вызове основной функции. Функция —это блок программы, который выполняет одно или несколько действий. Обычно функции вызываются другими функциями, но функция main() вызывается автоматически при запуске программы, каждая программа должна содержать эту функцию, причём только одну, нельзя назвать другую функцию так же.

Любая функция должна иметь тип возвращаемого значения, в нашем случае это слово int. Оператор return возвращает это значение.

Все функции начинаются с { и оканчиваются }. То, что располагается между ними называется телом функции.

Объект cout позволяет вывести сообщение на экран монитора. Близкий ему объект cin позволяет считывать данные. За этими объектами следует указание перенаправления выходного потока << или >>. Строка текста —набор печатных символов, обязательно заключается в кавычки. Символ // —позволяет вводить в программу коментарии —строки, поясняющие что-либо для программиста, но «не существующие» для компилятора.  ( /* …*/).

  1.  Переменные и константы.

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

Тип

Размер байт

Значения

bool

логические

1

True или  false

Unsigned short int

Беззнаковое короткое целое

Short int

Короткое целое

2

-32768—

Unsigned long int

Беззнаковое длинное целое

long int

Длинное целое

4

-2147483648—

int

Целое

-32768—

Unsigned int

Беззнаковое целое

char

символьное

значений

float

Действительное

,2е-38—,4е38

double

Двойной точности

,2е-308—,8е308

Существует функции sizeof(тип )

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

Рекомендации:

  •  Указывайте тип переменной перед именем при её определении.
  •  Используёте для определённости информативные имена.
  •  Помните, что в С++ различаются строчные и заглавные буквы.
  •  Уточняйте, сколько байт занимает в памяти каждый тип переменной, и какие значения он может хранить.
  •  Не используйте ключевые слова в качестве имён переменных.
  •  Не присваивайте беззнаковым переменным отрицательные значения.

В языке предусмотрена возможность создания псевдонима для фразы (например unsigned short int ) Это слово typedef.

Переполнение.

Символы.

Специальные символы.

Константа —число или  символьное значение, которое не может быть изменено в течении программы.

Определение константы :

  1.  директива #define Numstud 15
  2.  const int Numstud =15;

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

Enum  Color (Красный, …..Фиолетовый);

В языке С++ существуют выражения  x=a+b Смысл его в том, что значения находящиеся под символьным адресом a и b  складываются и результат сложения помещается в адрес x. ( = это оператор присваивания, оператор сравнения это ==).

Блоки ограничиваются фигурными скобками и выполняются как одно выражение.

Рекомендации:

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

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

Язык С++ позволяет сократить запись прибавления и уменьшения переменной на 1 (инкремент +, декремент -) d++; d--;--d,++d,. D+=1; Если знак операции стоит до переменной (префикс), если a=++x, то если x=5, то сначала x=6,  а потом a=x(6) , если же a=b++, (b=5) то а=5, а b=6.

Операторы отношений.

Имя

Оператор

Пример

Значение

Равно

==

100==50

==50

False

true

Не равно

!=

50==50

false

Больше

>

Больше или равно

>=

Меньше

<

Меньше или равно

<=

  1.  Оператор ветвления if.

Логические операторы.

Схема if( (x==5)

If (условие) выражение;

If(условие)

{

выражение 1;

выражение 2;

}

else

{

выражение 3;

выражение 4;

}

Логические операторы.

Оператор

Символ

А

С

Результат

И

&&

T

T

F

F

T

F

T

F

T

F

F

F

ИЛИ

||

T

T

F

F

F

T

T

F

T

T

T

F

НЕТ

!

T

F

F

T

Возможна запись: (выражение 1)?(выражение 2): (выражение 3);

Пример

#include<iostream.h>

int main(void)

{

int  fistNumb,secondNumb;

cout <<"Введите два числа\nПервое:";

cin >>fistNumb;

cout <<"\nВторое:";

cin >>secondNumb;

cout <<"\n";

if(fistNumb >= secondNumb)

{

if((fistNumb%secondNumb)==0)

{

if(fistNumb==secondNumb)

 cout<<"Число "<<fistNumb<<" равно "<<secondNumb<<"\n";

else

 cout<<"Число "<<fistNumb<<" делится без остатка на "<<secondNumb<<"\n";

 }

else

cout<<"Число "<<fistNumb<<" не делится без остатка на "<<secondNumb<<"\n";

}

else

 cout<<"Число "<<fistNumb<<" меньше чем "<<secondNumb<<"\n";

return 0;

}


  1.  Циклы. Счётчики. Указатели. память. размещение объектов в области динамической памяти. ссылки. Адреса. передача аргументов функции как ссылок. возвращение нескольких значений.
    1.  Циклы.

Цикл —это блок программы , который необходимо повторить несколько раз. Исторически первые циклы организовывались при помощи оператора безусловного перехода  goto;

#include <iostream.h>

int main(void)

{

int i=0;

loop:  i++;

cout << "счётчик ="<<i<<"\n";

if(i<5)

goto loop;

cout <<"конец работы\n";

return 0;

}

Использование оператора goto позволяет написать так называемую “спагетти” плохо читаемую и организованную программу. Поэтому последние 20 лет преподаватели убеждают студентов не использовать этот оператор.

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

#include <iostream.h>

int main(void)

{

int i=0;

while(i<5)

{

cout <<"счётчик = "<<i<<"\n";

 i++;

}

cout <<"конец работы\n";

return 0;

}

Та же программа написанная при помощи while().

Для того чтобы перейти на следующую итерацию цикла используется оператор continue, для прерывания цикла окончательно —break.

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

Для того? чтобы условие проверялось после выполнения цикла, используют конструкцию dowhile. Например расчёт суммы бесконечного ряда с точностью до eps=0.0003.(дом. Работа).

Оператор for позволяет объединить несколько операций по определению цикла в одной строке for(I=0;I<10;I++)

#include <iostream.h>

int main(void)

{

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

cout <<"счётчик = "<<i<<"\n";

cout <<"конец работы\n";

 return 0;

}

Цикл for работает в такой последовательности:

  1.  Присваивается начальное значение счётчику ( и другие начальные значения);
  2.  Вычисляется значение выражения на условия продолжения цикла;
  3.  Если условие возвращает true, то сначала выполняется тело цикла, а затем заданная операция с счётчиком.
  4.  шаги 2 и 3 повторяются на каждой итерации цикла.

#include <iostream.h>

int main(void)

{int I,j;

for ( i=0,j=0;i<3;i++,j++)

cout <<"i= "<<i<<" j="<<j<<"\n";

I=j=10;

cout <<”I=”<<I<<” j=”<<j<<”\n”;

cout <<"конец работы\n";

return 0;

}

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

  1.  Оператор switch.

Оператор множественного ветвления.

#include <iostream.h>

int main(void)

{   int number;

cout <<"ВВедите число между 1 и 5: ";

 cin >> number;

cout <<"\n";

switch(number)

{

case 0: cout<<" Очень мало извините!\n";break;

 case 2: cout<<"Уже лучше.\n";

case 3: cout<<"Просто хорошо.\n";

case 4: cout<<"Замечательно!\n";

 case 5: cout<<"Класс!!!\n";

case 1: cout<<"Едва хватит!\n";break;

 default: cout<<"Слишком много, так не бывает\n";break;

}

return 0;

}

  1.  Указатели.

Указатель —это переменная, в которой записан адрес ячейки памяти.

Так как указатель это обычная переменная, то имена им даются такие же, иногда программисты добавляют к ним букву «р». (pNumber).

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

Что бы присвоить указателю адрес переменной необходимо использовать символ &.

Что бы получить результат по адресу, куда ссылается указатель (разименовать) перед именем указателя ставиться *.

int *a,b,c=0;

b=5;

a=&b;

c=(*a)+1;

*a=7;

результат:

b=7;

c=6;

ошибка:

c=*(a+1);

Наиболее часто указатели используются для:

  •  Размещения данных в свободных областях памяти и доступа к ним.
  •  Доступам к переменным и функциям классов.
  •  Передачи параметров функции по ссылке и получении более, чем одного результата.
  •  Работы с массивами.

Существует условное разделение памяти с которой работает программа на несколько областей:

  •  Область глобальных переменных.
  •  Свободная или динамически распределённая память.
  •  Регистровая память.
  •  Сегменты программы.
  •  Стековая память.

Локальные переменные и параметры функций располагаются в стековой памяти, программный код храниться в сегментах, глобальные переменные в области глобальных переменных. Локальные переменные —это переменные которые «видит» только часть программы, глобальные —доступны из любой точки программы.  Свободная память распределяется между динамическими объектами. Ячейкам динамической памяти нельзя присвоить имя (если это произойдёт, то это уже стековая память), к ним можно обратиться по адресу, к тому же адрес можно передать в любую область программы, следовательно данные, хранящиеся по этому адресу доступны везде.

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

Int I;

Int  &ri=I; —это ссылка на I, ссылка обязательно должна быть инициализирована, то есть иметь значение, то есть куда-то ссылаться, однажды инициализировав ссылку ей нельзя присвоить другое значение. Нельзя ri=j; так как ri —это синоним i.  

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

Для выделения памяти в области динамического распределения используется оператор new . Для этого подходит строка (тип данных —int) :

Int *page = new int;

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

Int *a, b, *c;

*a=b;

c=a++;

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

Функции. Объявление и определение функции. выполнение функции. Локальные и глобальные переменные. Аргументы функции —фактические и формальные. Перегрузка функций. возвращаемые значения. Массивы.

  1.  Функции.
    1.  Использование функций.

Функция по своей сути —это подпрограмма, которая может манипулировать данными и возвращать некоторое значение. Каждая программа имеет хотя бы дону программу —main().

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

Различают два вида функций : встроенные и определённые пользователем. Встроенные функции являются составной частью пакета компилятора и предоставляются фирмой-изготовителем. Нестандартные функции разрабатываются программистом.

Функции возвращают либо некоторое реальное значение либо некоторое значение типа void (то есть «ничего»).

Функция так же может принимать некоторые значения, то есть иметь параметры.

В общем виде объявление функции может иметь вид:

Int MyFunction(int some Value, float SameFloat);

Вызов функции имеет вид:

Rezult=MyFunction(12.4,4.7);

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

Существуют три способы объявления функции:

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

Прототип функции имеет вид:

В прототипе используется список формальных параметров.

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

Пример: заголовок функции складывающей два числа.

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

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

Пример:

#include <iostream.h>

int Add(int x, int y);

int Add(int x,int y)

{

cout << “В Add складываются “ <<x<< “и”<<y<<”\n”;

return (x+y);

}

int main()

{  int a,b,c;

cout <<”Введите два числа:\n”;

cin >>a;

cin >> b;

c=Add(a,b);

  cout <<”a+b=”<<c<<”\n”;

return 0;

}

Макрофункции

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

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

Пример 3.

/* текст программы до обработки препроцессором */

#include<stdio.h>

#define SQUARE(x)  x*x

void main(void)

{

int y=5,rez;

printf("квадрат y равен %d\n",SQUARE(y)); printf("квадрат y+2 равен %d\n",SQUARE(y+2)); printf("частное равно %f\n",100./SQUARE(y));

}

  1.  Перегрузка функций.

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

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

Inline int Area(int,int);

Возмоожны рекурсивные функции, то есть функции, вызывающие самое себя.

Пример.

#include <iostream.h>

int fib(int n);

int fib(int n)

{ if (n<3)

 return 1;

else

return (fib(n-2)+fib(n-1));

}

int main(void)

{

int i,rez;

cout <<"введите число Фибоначи = ";

cin >> i;

rez=fib(i);

cout <<i<<"-oe число Фибоначи ="<<rez<<"\n";

return 0;

}

Результат

введите число Фибоначи = 6

-oe число Фибоначи =8

  1.  

Возврат более одного результата.

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

Пример.

#include<iostream.h>

#include<conio.h>

//  #include"sqwere.cpp"

#include <math.h>

int sqwere(float a,float b, float c,float *x1,float *x2);

// объявление функции, ниже идут заголовок и тело этой функции

int sqwere(float a,float b, float c,float *x1,float *x2)

{ float   d;

d=b*b-4.*a*c;

if(d<0.)return 0;

*x1=(-b-sqrt(d))/(2.*a);

*x2=(-b+sqrt(d))/(2.*a);

if(d==0.)return 1;

if(d>0)  return 2;

}

int main(void)

{

float a,b,c,x1,x2;

int rez;

cout <<"Введите числа\n a:";

cin >>a;

cout <<"\n b=:";

cin >>b;

cout <<"\n c=:";

cin >>c;

// вызов функции с фактическими параметрами

rez=sqwere(a,b,c,&x1,&x2);

switch(rez)

{ case 0: cout << "корней нет \n";break;

case 1: cout << "корнь один = "<< x1 <<" \n";break;

case 2: cout << "корней два x1= "<< x1 <<"  x2= "<<x2<<"\n";break;

}

return 0;

}

Та же самая программа написана при помощи ссылок, найдите отличия

#include<iostream.h>

#include<conio.h>

#include <math.h>

int sqwere(float a,float b, float c, float &x1,float &x2);

int sqwere(float a,float b, float c,float &x1,float &x2)

{ float   d;

d=b*b-4.*a*c;

if(d<0.)return 0;

x1=(-b-sqrt(d))/(2.*a);

x2=(-b+sqrt(d))/(2.*a);

if(d==0.)return 1;

if(d>0)  return 2;

}

int main(void)

{

float a,b,c,x1,x2;

int rez;

cout <<"Введите числа\n a:";

cin >>a;

cout <<"\n b=:";

cin >>b;

cout <<"\n c=:";

cin >>c;

rez=sqwere(a,b,c,x1,x2);

switch(rez)

{ case 0: cout << "корней нет \n";break;

case 1: cout << "корнь один = "<< x1 <<" \n";break;

case 2: cout << "корней два x1= "<< x1 <<"  x2= "<<x2<<"\n";break;

}

return 0;

}

  1.  


Массивы. Элементы массива. инициализация массива. объявление массива. Динамические и статические массивы. Многомерные массивы. Строковые.

Массивы.

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

Описание массива:

Доступ к элементам массива осуществляется по адресации, относительно первого элемента, то есть, если необходимо обратиться к 10 элементу, надо написать IntArrey[9] или IntArrey+9; (Первый элемент имеет номер 0 или смещение 0 от начала.)

Возможна инициализация массива

Int Arrey[5]={10,20,30,40,50};

Существуют много мерные массивы, в этом случае указывается размерность по всем размерностям: int Arrey2[5][3]; Инициализация такого массива представляется в виде:

Int Arrey[5][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12},{0,1,0}};

Int Arrey[5][3]={1,2,3,4,5,6,7,8,9,10,11,12,0,1,0};

При этом описании Существует массив Arrey[5] , который содержит адреса начала каждой строки, Если мы хотим использовать указатель на этот массив необходимо обращение **Arrey;

Для объявления массивов можно использовать область динамической памяти, в этом случае описание массива будет иметь вид:

Int *Arrey;

Arrey=(int*)malloc(n*sizeof(int));

Пример программы, работающей с массивами.

#include<iostream.h>

#include <alloc.h>

#include <stdlib.h>

#include <time.h>

#include <math.h>

int vvodArrey(int n,int m,int *Arrey,int zz);

int exitArrey(int n,int m,int *Arrey);

int* sunArrey(int n,int m,int *Arrey1,int*Arrey2);

int vvodArrey(int n,int m, int *Arrey,int zz)

{ int i,j;

randomize();

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

{ for(j=0;j<m;j++)

 *(Arrey+i*m+j)=random(zz);

}

return 0;

}

int exitArrey(int n,int m, int *Arrey)

{ int i;

for(i=0;i<n*m;i++)

 (((i+1)%m)==0)? cout<<*(Arrey+i)<<"\n" : cout<<*(Arrey+i)<<"\t";

return 0;

}

int* sunArrey(int n,int m,int *Arrey1,int*Arrey2)

{

int*Arrey;

int i,j;

Arrey=(int*)malloc(sizeof(int)*n*m);

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

for(j=0;j<m;j++)

*(Arrey+i*m+j)=*(Arrey1+i*m+j)+(*(Arrey2+i*m+j));

return Arrey;

}

int main(void)

{

int *ArreyF,*ArreyS,*ArreyRez,n,m,size=100;

cout <<"Введите размерность массива\n по строкам:";

cin >>n;

cout <<"по столбцам : ";

cin >>m;

if ((ArreyF=(int*)malloc(sizeof(int)*n*m))==0)

{ cout <<" нет памяти\n";return -1;}

if ((ArreyS=(int*)malloc(sizeof(int)*n*m))==0)

{ cout <<" нет памяти\n";return -1;}

vvodArrey(n,m,ArreyF,size);

vvodArrey(n,m,ArreyS,size/2.);

ArreyRez=sunArrey(n,m,ArreyF,ArreyS);

cout <<"******--1--********\n";

exitArrey(n,m,ArreyF);

cout <<"******--2--*********\n";

exitArrey(n,m,ArreyS);

cout <<"******--3--*********\n";

exitArrey(n,m,ArreyRez);

return 0;

}

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

Введите размерность массива

по строкам:    2

по столбцам : 3

******--1--********

    47      73

     85      53

******--2--*********

    23      36

     42      26

******--3--*********

    70      109

    127     79

Введите размерность массива

по строкам:   1

по столбцам : 5

******--1--********

    44      39      57      46

******--2--*********

    22      19      28      23

******--3--*********

   66      58      85      69

  1.  

***Домашнее задание: переписать программу со статическим  выделением памяти.

  1.  
    Структуры. Доступ к элементам структуры. Инициализация. Выделение памяти. работа с динамическими типами данных . работа со  стеком, очередью, деком, деревом. (Самостоятельное изучение)
    1.  Создание новых типов

Тип переменной определяет: её размер в памяти, тип данных, которые она может хранить и операции, которые можно производить с этой переменной.

Тип данных является категорией. В языке С++ программист может создать любой тип данных на основе базовых типов.  Новые типы данных необходимо создавать для решения конкретных практических задач. Например: реализация работы деканата.

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

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

. установить шаблон для структуры

. объявить переменную, соответствующую этому шаблону

. осуществить доступ к компонентам структуры.

  1.                      Шаблон структуры

Шаблон - это схема, описывающая содержание структуры. Установка структурного шаблона телефонный справочник:

    struct sprav {

char fio[20];

long num;

};

Данный шаблон описывает структуру с именем  типа  структуры sprav, состоящую из двух компонентов: строки fio и целой переменной num типа long. Имя типа структуры sprav необязательно и  используется  для ссылки на эту структуру. Компоненты структуры - данные любого типа, включая и другие структуры. Имя внутри структуры может быть  таким  же,  как  имя объекта вне структуры. Если шаблон описан внутри функции - он доступен только  этой функции, если шаблон описан вне функции - он  доступен любой функции программы. Установка шаблона не вызывает никаких действий в программе.

  1.                   Структурные переменные

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

. Установить структурный шаблон:

    struct sprav {

char fio[20];

long num;

};

  Объявить простую переменную, массив структур, указатель на структуру:      struct sprav tel1, tel2[5], *tel3;

2. Установить структурный шаблон с помощью  макроопределения:

    #define SPRAV struct sprav

SPRAV {

char fio[20];

long num;

};

  Объявить переменные:

    SPRAV sp1, sp2[6], *sp3;

3. Объявить переменные одновременно с установкой шаблона (если на данную структуру вы больше не ссылаетесь):

    struct {

char fio[20];

long num;

} tel1, tel2[3], *tel3;

4. Ввести новый тип данных (TEL)-структура определенного вида:

    typedef struct {

char fio[20];

long num;

} TEL;

  Объявить переменные нового типа:

    TEL tel1, tel2[6], *tel3;

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

  1.                    Инициализация структуры                

Инициализировать можно только внешние или статические структуры.

    static struct {

char fio[20];

long num;

} tel[2]={

"Иванов Ф.А.", 456756,

"Петров В.П.", 632345

};

 Доступ к компонентам структуры

Доступ к компонентам структуры продемонстрируем с помощью примеров.

Пример 1.

/* Обращение к элементам структуры через имя переменной */

#include <stdio.h>

#include <conio.h>

void main(void)

{

struct{

char fio[20];  /* фамилия */

long num;      /* телефон */

} tel1, tel2;

clrscr();

puts("введите фио абонента-");

gets(tel1.fio);

puts("введите его номер-");

scanf("%ld",&tel1.num);

tel2=tel1;  /* нельзя так же сравнивать структуры */

puts("Введено:");

printf("Фамилия :%s   номер: %ld\n",tel2.fio,tel2.num);

}

Пример 2.

/* Динамическое выделение памяти для структуры */

/* Обращение к элементам структуры через указатель */

#include <stdio.h>

#include <conio.h>

#include <alloc.h>

struct sprav {

char fio[20];

long num;

};

void main(void)

{

struct sprav *tel1, *tel2;

clrscr();

/* Выделение памяти для структуры */

tel1=(struct sprav *)malloc(sizeof(struct sprav));

tel2=(struct sprav *)malloc(sizeof(struct sprav));

puts("введите фио абонента-");

gets(tel1->fio);

puts("введите его номер-");

scanf("%ld",&tel1->num);

*tel2= *tel1;

puts("Введено:");

printf("Фамилия :%s   номер: %ld\n",(*tel2).fio,(*tel2).num);

}

                     Массив структур

Пример 3.

/* Массив структур. Обращение к элементам структуры через */

/* имя элемента массива */

#include <stdio.h>

#include <conio.h>

#include <string.h>

#define SPRAV struct sprav

void main(void)

{

SPRAV{

char fio[20];

long num;

};

SPRAV tel[5];    /* массив структур - 5 элементов */

char fio_tek[20];

int i;

clrscr();

/* ввод данных в массив структур */

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

{

puts("введите фио абонента-");

gets(tel[i].fio);

puts("введите его номер-");

scanf("%ld",&tel[i].num);

getchar();

}

puts("Выбор телефона по фамилии");

gets(fio_tek);

/* поиск структуры по фамилии абонента */

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

if(!strcmp(fio_tek,tel[i].fio)) break;

if(i!=5)    /* цикл закончен по break */

printf("номер абонента %s равен %ld\n",fio_tek, \

tel[i].num);

else        /* цикл выполнился полностью */

puts("Абонент не найден");

}

Пример 4.

/* Массив структур. Память выделяется динамически. */

/* Обращение к элементам структуры через указатель */

#include <stdio.h>

#include <conio.h>

#include <string.h>

#include <alloc.h>

typedef struct{

char fio[20];

long num;

} TEL;

void main(void)

{

TEL *tel;

char fio_tek[20];

int i;

clrscr();

/* Выделение памяти для массива - 3 элемента */

tel=(TEL *)malloc(sizeof(TEL)*3);

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

{

puts("введите фио абонента-");

gets((tel+i)->fio);

puts("введите его номер-");

scanf("%ld",&(tel+i)->num);

getchar();

}

puts("Выбор телефона по фамилии");

gets(fio_tek);

for(i=0; i<5; i++,tel++)

if(!strcmp(fio_tek,tel->fio)) break;

if(i!=5)

printf("номер абонента %s равен %ld\n",fio_tek, \

tel->num);

else

puts("Абонент не найден");

}

             Передача структуры в функцию

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

Пример 5.

/* Передача структуры в функцию через указатель на структуру */

/* Определение комплексного числа через структуру и действия */

/* над комплексными числами ( ввод, вывод, вычисление суммы) */

#include <stdio.h>

typedef struct { float a;   /* действительная часть */

float b;   /* мнимая часть */

} COMPLEX;

void vvod(COMPLEX *,float,float);      

void sum(COMPLEX *,COMPLEX *,COMPLEX *);

void out(COMPLEX *);

void main(void)

{

COMPLEX x,y,z;

vvod(&x,2.5,6.7);

vvod(&y,6.89,8.45);

puts("Введены числа:");

out(&x);

out(&y);

sum(&x,&y,&z);

puts("Сумма комплексных чисел равна:");

out(&z);

}

/* Вывод комплексного числа */

void out( COMPLEX *p)

{

printf("(%.2f,%.2f)\n", (*p).a,(*p).b);

return;

}

/* Вычисление суммы двух комплексных чисел */

void sum(COMPLEX *p1,COMPLEX *p2,COMPLEX *p3)

{

(*p3).a=(*p1).a+(*p2).a;

(*p3).b=(*p1).b+(*p2).b;

return;

}

/* Ввод значений для элементов структуры */

void vvod(COMPLEX *p,float a, float b)

{

p->a=a;

p->b=b;

return;

}

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

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

Пример 6.

/* Даны четыре точки - центры четырех окружностей.    Заполнить структуру окружность, если все окружности    проходят через начало координат.    */

#include<conio.h>

#include<stdio.h>

#include<math.h>

#include<stdlib.h>

struct POINT {

float x;

float y;

};

struct CIRCLE {

struct POINT point;  /* вложенная структура */

double r;

} circle[2], *p;

void main (void)

{

int i,j;

float a,b,c,d;

clrscr();

gotoxy(17,1);

cputs("ВВЕДИТЕ КООРДИНАТЫ ТОЧЕК :\r\n");

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

{

cprintf ("\n\n ВВЕДИТЕ X: ");

cprintf ("X[%d]= ",i+1);

cscanf("%f",&circle[i].point.x);

cprintf ("\n ВВЕДИТЕ Y: ");

cprintf ("Y[%d]= ",i+1);

cscanf ("%f",&circle[i].point.y);

}

p=circle;

gotoxy(17,12);

cputs("РЕЗУЛЬТАТ:\r\n\n");

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

{

a=p->point.x;

b=p->point.y;

c=sqrt(a*a+b*b);

p->r=c;

cprintf("\nРАДИУС : %lf ЦЕНТР (%f,%f)\r\n",p->r,a,b);

p++;

}

  1.  
    Базовые классы. Классы и члены. Получение доступа к членам класса. Ограничение доступа к членам класса. определение методов класса. Конструкторы и деструкторы. Вложенные классы. структуры.
    1.  Классы.

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

Для объявления класса используется ключевое слово class. Пример:

Class Cat

{

unsigned int Age;

unsigned int Weight;

void Meow();

};

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

Определение объектов происходит так:

Cat Vaska;

  1.  Доступ к членам класса.

Существует оператор прямого доступа (.)

Vaska.Weight =5;

Vaska.Meow();

Рекомендации:

  1.  используйте ключевое слово class для объявления класса.
  2.  используйте оператор прямого доступа к членам класса и его методам.
  3.  не путайте объявление с определением.
  4.  не путайте класс с объектом.
  5.  не присваивайте значения классу.

В объявления класса используются и другие ключевые слова.  Двумя важными из них являются public и private. Все данные-члены и методы являются закрытыми по умолчанию, то есть к ним можно получить доступ только методами самого класса. Открытые члены доступны всем функциям программы.

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

Class Cat

{

unsigned int Age;

unsigned int Weight;

void Meow();

};

а затем в main()

cat Vaska;

Vaska.Age=5;

Вызовет ошибку компиляции, так как все члены класса Сat закрыты, а присвоение идёт во внешней к классу функции.

Поэтому необходимо сделать:

Class Cat

{public:

unsigned int Age;

unsigned int Weight;

void Meow();

};

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

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

#include<iostream.h>

#include <alloc.h>

#include <math.h>

#include <stdlib.h>

class Mass

{

int *Arrey;

int n;

public:

int getSize();

void putSize(int ss);

void getArrey(int ras);

int putArrey();

void del();

};

void Mass::getArrey(int ras)

{ int i;

randomize();

Arrey = new int[n];

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

(Arrey+i)=random(2*ras)-ras;

}

int Mass::putArrey()

{ int i;

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

cout<<*(Arrey+i)<<"\t";

cout <<"\n";

return 0;

}

int Mass::getSize()

{  return n;

}

void Mass::putSize(int ss)

{n=ss;

}

void Mass::del()

{delete[] Arrey;

}

int main(void)

{

int ss,m,size=100;

Mass Arrey;

cout <<"Введите размерность массива\n по строкам:";

cin >>ss;

Arrey.putSize(ss);

cout << "размерность массива = "<<Arrey.getSize()<<"\n";

Arrey.getArrey(100);

Arrey.putArrey();

Arrey.del();

Arrey.putArrey();

return 0;

}

Любую переменную можно инициализировать, то есть присвоить ей значения, для инициализации объектов класса существуют специальные функции —конструкторы. Имя конструктора совпадает с именем класса, при необходимости он может принимать параметры, но не может возвращать значения даже типа void. Для примера: Arrey(). Если пользователь не объявляет конструктор, то существует конструктор по умолчанию, который ничего не делает, но он есть. Соответственно существует функции уничтожающая объекты класса —деструктор, она очень похожа на конструктор по своим свойствам. Зачастую наличие конструктора (стандартного, который ничего не делает) необходимо по протоколу. Имя деструктора ~Arrey  .

Домашняя работа написать предложенную программу, так что бы в ней были конструктор с 3-мя параметрами и деструктор.

  1.  
    Перегрузка функций-членов. Инициализация объектов. Перегрузка операторов.  Операторы преобразований. Наследование.
    1.  Дополнительные возможности использования функций.

Дополнительные возможности позволяют:

  •  Перегружать функции-члены:
  •  Перегружать операторы.
  •  Создавать функции для поддержания классов с динамическим выделением памяти для переменных.

Мы уже говорили о перегрузке функций на занятии посвящённом функциям.Функции-члены можно перегрузить точно так же.

#include<iostream.h>

class Rect

{ public:

Rect(int width,int height);

~Rect() {}

void DrawRect(int aWidth,int aHeight);

void DrawRect();

private:

int itsWedth;

int itsHeight;

};

Rect::Rect(int wedth,int height)

{

itsWedth=wedth;

itsHeight=height;

}

void Rect::DrawRect()

{

DrawRect(itsWedth,itsHeight);

}

void Rect::DrawRect(int aWedth,int aHeight)

{

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

{

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

cout<<"*";

cout << "\n";

}

}

int main(void)

{

Rect theRect(30,5);

cout <<"Печать того, что уже есть\n";

theRect.DrawRect();

cout <<"Печать нового\n";

theRect.DrawRect(40,10);

return 0;

}

  1.  

Таким же образом можно перегружать конструкторы.

В конструкторе можно инициализировать члены не в теле, а в момент инициализации самого конструктора, синтаксис выглядит следующим образом:

Rect::Rect(int wedth,int height):

ItsWedth(wedth),

ItsHeight(height) {}

Особенностью функции, которая имеет параметр по умолчанию является возможность ситуации: если параметр не указывается в скобках при вызове, то он имеет это значение. Значения параметров по умолчанию могут задаваться справа налево, пример: void DrawRect (int aWidth, bool Use = false, int aHeight);не может быть использован, так как самый правый параметр по умолчанию не имеет значения.

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

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

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

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

  1.  Функции  —друзья.

Дружественная функция —это функция, которая не является методом класса, но имеет доступ к собственным и защищённым элементам класса. Функция не может быть другом без согласия класса, то есть она должна быть описана в теле класса с помощью специального слова friend.

Дружественная функция при вызове не получает указателя *this  Объекты классов должны передаваться явно через аппарат параметров. При её вызове нельзя использовать аппарат вызова метода класса. Использование дружественных функций позволяет упростить интерфейс между классами.  Можно сделать все функции класса Y друзьями класса X:

Class Y

{

void f1(X&);

void f2(X*);

};

class X

{

friend Y;

int I;

void f3();

}

Перегрузка операторов.

Форма для перегрузки операторов:

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

Const класс & класс::operator …();

Перегрузка оператора с двумя операндами требует наличия параметров (константные ссылки на объекты этих классов):

Класс класс::operator + (const класс &rhs) —заголовок и след. Описание

Класс operator + (const класс &)

Рекомендации:

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

#include<iostream.h>

class V_3d

{

double x,y,z;

public:

V_3d(const V_3d &v1);      //  конструктор -копировщик

V_3d(double x1=0,double y1=0,double z1=0):

// конструктор со значениями по умолчанию

x(x1),y(y1),z(z1) {};              // инициация переменных-членов

void print();                           // печать объекта класса

V_3d operator +(V_3d t);              // перегрузка оператора +

V_3d operator =(V_3d t);                // перегрузка оператора =

friend ostream &operator <<(ostream &stream,V_3d t);

friend istream &operator >>(istream &stream,V_3d &t);

double mod();                           // функция модуля

};

V_3d:: V_3d(const V_3d &v1)

{  x=v1.x;

y=v1.y;

z=v1.z;

}

void V_3d::print()

{

cout<<"x="<<x<<" y="<<y<<" z="<<z<<"\n";

}

V_3d V_3d:: operator +(V_3d t)

{

V_3d c;

c.x=x+t.x; c.y=y+t.y; c.z=z+t.z;

return c;

}

V_3d V_3d:: operator =(V_3d t)

{

x=t.x; y=t.y; z=t.z;

return *this;

}

ostream &operator <<(ostream &stream,V_3d t)

{

stream<<"x="<<t.x<<",";

stream<<"y="<<t.y<<",";

stream<<"z="<<t.z<<"\n";

return stream;

}

istream &operator >>(istream &stream,V_3d &t)

{

stream >>t.x>>t.y>>t.z;

return stream;

}

double V_3d:: mod()

{

return x*x+y*y+z*z;

}

void main(void)

{

V_3d a;

V_3d b(4,7,10);

V_3d c(b);

cout << "координаты вектора b: \n";

cout <<b;

cout << "координаты вектора c: \n";

c.print();

cout <<"введите координаты вектора a:\n";

cin >>a;

cout << "координаты вектора а: \n";

cout <<a;

c=a+b;

cout << "координаты вектора a+b \n";

c.print();

}

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

координаты вектора b:

x=4,y=7,z=10

координаты вектора c:

x=4 y=7 z=10

введите координаты вектора a:

-1    3   6

координаты вектора а:

x=-1,y=3,z=6

координаты вектора a+b

x=3 y=10 z=16

Конструктор-копировщик.

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

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

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

 

  1.  
    Наследование   Закрытый. Защищённый Форматированный ввод. Ввод-вывод в файл.
    1.  Наследование.

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

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

Когда мы говорим, что нечто является видом чего-либо, мы подразумеваем большую детализацию.

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

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

Синтаксис : (открытое наследование):

Class имя_класса :< режим доступа> имя_базового_класса.

В описании базового класса появляется новое слово:protected, так как данные под словом private не доступны для наследования.

Какой бы доступ не был определён для наследования данные, описанные в базовом классе как private не доступны для производного класса.

#include <iostream.h>

class base

{  int x;

 public:

 void setx(int n){x=n;}

 void prinx(){cout<<"x="<<x<<"\n";}

};

class deriv : private base

{

int y;

public:

void setxy(int n,int m){setx(n);y=m;}

void prinxy(){prinx();cout<<"y="<<y<<"\n";}

};

void main(void)

{

deriv ob;

cout <<"результат работы программы: \n" ;

ob.setxy(10,25);

ob.prinxy();

}

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

x=10

y=25

Инициализация наследуемых классов.

Все действия по инициализации объектов выполняет конструктор порождённого класса.

Если конструктору базового класса требуются аргументы, то они должны быть указаны в списке аргументов конструктора. В общем виде конструктор имеет вид:

Конструктор_производного_класса(список аргументов): базовый_класс1( список аргументов)…базовый_класс_N (список аргументов) { тело конструктора}

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

#include <iostream.h>

class B1

{   public:

B1(){cout<<"работа конструктора B1\n";}

~B1(){cout<<" работа деструктора B1\n";}

};

class B2

{ public:

B2(){cout<<"работа конструктора B2\n";}

~B2(){cout<<" работа деструктора B2\n";}

};

class D:public B1,public B2

{

public:

D(){cout<<"работа конструктора D\n";}

~D(){cout<<" работа деструктора D\n";}

};

void main(void)

{

cout <<"результат работы программы: \n" ;

D ob;

cout<<"*********************\n" ;

}

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

работа конструктора B1

работа конструктора B2

работа конструктора D

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

работа деструктора D

работа деструктора B2

работа деструктора B1

  1.  

Конструкторы базового класса должны быть объявлены с атрибутами protected или public для того, чтобы обеспечить возможность их вызова из класса-наследника.

  1.  Потоки.

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

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

В С++ используется ОО подход к реализации обмена данными с буферными потоками.

  •  Класс streambuf управляет буфером, его методы позволяют наполнить, опорожнить и очистить буфер.
  •  Класс ios является базовым для классов ввода-вывода.
  •  Классы istream и ostream являются произвольными от класса ios и отвечают соответственно за потоковый ввод и вывод данных.
  •  Класс iosteam является произвольным от istream и ostream и обеспечивает методы ввода-вывода для печати на экран.
  •  Классы fstream используются для ввода-вывода из файла.

В файле iostream.h определены классы iostream и ostream, именно то, что эти операторы ввода-вывода работают с классами, позволяет перегружать их для работы с классами пользователя.  Оператор cin  является глобальным объектом и перегружается таким образом, что можно вводить различные типы данных.

В частности при помощи этого объекта модно ввести тип *char, то есть строку символов.  Можно использовать дополнительные методы этого класса, что бы более полно контролировать ввод-вывод. Пример : ввод одного символа можно реализовать при помощи метода cin.get()>>

#include <iostream.h>

#include <math.h>

int main(void)

{

char ch;

while((ch=cin.get())!=EOF)

{

cout << "ch: "<<ch<<endl;

}

cout <<"\n конец \n";

return 0;

} //ввод EOF —Ctrl+Z   

cin.get() возвращает не объект типа iostream, а тип int.  А если вы используете параметр cin.get(a), то возвращается объект iostream, а параметру присваивается значение из потока. Для работы со строками подойдёт как оператор ввода <<, так и методы get() и getline(). Для ввода строки можно использовать get()  и getline() с тремя параметрами:

  •  указатель на массив символов.
  •  максимально-возможное количество символов в строке, включая концевой символ;
  •  разделитель строк.

Cin.get(str,80,”\n”);

Разница между этими двумя функциями в том, что первая оставляет символ разделения строк в буфере , а вторая удаляет его.

Существует ещё несколько дополнительных методов:

Ignore(80,”\n”) параметры —сколько пропускается символов без чтения и разделитель.

 Peek() —просматривает, но не считывает очередной символ.

 Putback() —вставляет символ в поток ввода.

#include <iostream.h>

#include <iomanip.h>

int main(void)

{

char ch;

cout <<"введите фразу: ";

while(cin.get(ch))

{

if(ch=='!') cin.putback('$');

else cout <<ch;

while(cin.peek()=='#')

 cin.ignore(1,'\n');

}

return 0;

}

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

введите фразу: кен!!gh#ghj##sdfdgfh

//  кен$$ghghjsdfdgfh

Для объекта cout существуют соответствующие методы put() и write().

Cout.put() и cout.write(мах. Размер вывода);

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

Cout.Width()  —устанавливается точное значение ширины поля ввода.

Cout.fill(‘*’) —заполнение пустых позиций поля * (можно другой символ).

Флаги позволяют оформить вывод:

  1.  Управление выводом данных:
  •  
  •  \n—новая строка.
  •  \t—табуляция (несколько пробелов).
  •  \\—обратный слеш.
  •  \a—звуковой сигнал.

Манипуляторы, не требующие включения файла iomanip.h:

  •  flush—очищает буфер ввода
  •  endl —вводит конец строки
  •  oct —- ричное представление числа
  •  dec —- ричное представление числа
  •  hex —- ричное представление числа

Манипуляторы, требующие включения файла iomanip.h:

  •  setw(ширина) —ширина поля вывода.
  •  setfill(символ) —символ заполнения пустых позиций.
  •  setprecision (точность) —количество знаков после ,.

Все манипуляторы, кроме flush, end, setw() остаются включенными на протяжении работы всей программы, если не будет других установок.

Флаги используют формат написания setf(ios::флаг1 | ios::флаг2). (дом. Работа)

  1.  
    Использование файлов для ввода-вывола.
    1.  Использование файлов для ввода- вывода информации.

Объекты, создаваемые для считывания или записи данных в файлы, называются ofstream.  Они являются производными от уже рассмотренного класса iostream. Чтобы приступить к записи в файл необходимо создать объект  ofstream, а потом связать его с определённым файлом на диске.  Для этого требуется включение в программу файла fstream.h (в этом случае подключение файла iostream.h не нужно).

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

  •  Eof() — возвращает TRUE, если в потоке встречается символ EOF (конец файла).
  •  Bad() — возвращает TRUE при попытке ошибочной операции.
  •  Fail() —возвращает TRUE как и bad(), а также если операция невыполнима сейчас.
  •  Good() —возвращает TRUE, когда всё идёт хорошо.

Для открытия файла myfail.txt  нужно^

  •   Создать поток:
  1.  для ввода ifstream f1;
  2.  для вывода — ofstream f2;
  3.  для ввода и вывода — fstream f3;
  •   Привязать поток к файлу:
  1.  open(имя_файла, режим, доступ);
  2.  имя файла — параметр типа *char, может содержать полный путь к файлу .
  3.  режим — тип int, значения см. таблице.
  4.  доступ —тип int, задаёт права доступа к файлу (см. таб.)

Режим:

Ios::app

Добавление в конец файла, только для вывода

Ios::ate

Позиционирование в конец файла

Ios::binary

Открытие в двоичном режиме (ввод не текста)

Ios::in

Открытие файла для ввода

Ios::nocreate

Открыть уже существующий файл

Ios::noreplase

Создать новый, если уже есть — ошибка

Ios::out

Открытие файла для вывода

Ios::trunc

Открытие существующего файла с уничтожением информации в нём, длина файла ==0.

Атрибуты:

0

Обычный файл, со свободным доступом, по умолчанию

1

Только для чтения

2

Скрытый файл

4

Системный файл

8

Архивный файл

При использовании параметров по умолчанию можно сделать так:

F2(“d:\\user\\bs-11\\f2.dat”); или

Ifstream f1(“test.txt”);

Ofstream f2(“rez.txt’);

При окончании работы с программой необходимо использовать метод close(), закрытие файла.

F1.close();

F2.close();

#include<fstream.h>

#include<iostream.h>

void main(void)

{ char ch;

int num=0;

char buffer[255];

 ofstream fout("rez.txt");

cout<<"результат работы программы: \n";

 cin.ignore(1,'\n');

 cin.getline(buffer,255);

 fout<<buffer<<"\n";

 fout.close();

 ifstream fin("rez.txt");

 fin.unsetf(ios::skipws);

 while(!fin.eof())

 {fin>>ch;

 cout<<ch;

 num++;

 }

 cout <<"\n файл закончился\n число символов :"<<num<<"\n";

 fin.close();

}

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

укнеку нгшгнш №;%435435прорлорл рорлордло олодлод

кнеку нгшгнш №;%435435прорлорл рорлордло олодлод

файл закончилс

число символов :50

  1.  

Строки.

В языке С++ не существует тип данных —строковые, этот тип инициализируется одномерным массивом элементов типа char.

Пример:  char *Family=”Орешкина\0”; char Kreek[]=” Helloy, Word!”;

#include<iostream.h>

#include <alloc.h>

#include <string.h>

#include <math.h>

void srav(char* stroka,char* string);

void srav(char* stroka,char* string)

{int i;

i=strcmp(stroka,string);    // сравнение двух строк

if(i==0)i=0;

else i=(i<0)?i=-1:i=1;

cout<<"1-ое: "<<stroka<<",2-ое:"<<string<<"\n";

switch(i)

{

case 0: cout <<"они равны\n";break;

case-1: cout <<"1-ое меньше\n";break;

case 1: cout <<"1-ое больше\n";break;

}

}

int main(void)

{

char stroka[80],*ptr,string[80],c='*';

int i;

cout<<"/*результат работы программы\n";

cout <<"Введите строку:";

cin >>stroka;

i=strlen(stroka);

cout<<"длина строки "<< stroka<< "="<<I << endl;

cout <<"_______"<< stroka<<"\n";

 strcpy(string,stroka);  // копирование второй строки в первую

cout <<"*******"<< string<<"\n";

 strcat(stroka,"* Это Я") ; // добавление в первую строку второй

cout << "^^^^^^^"<<stroka<<"\n";

srav(string,stroka);

srav(stroka,stroka);

srav(stroka,string);

 ptr = strchr(stroka, c);  // проверка на вхождение символа.

if (ptr)

cout << "Символ "<< c <<" находится на " << ptr-stroka+1 << "позиции\n" ;

else

cout<<"Этого символа нет в строке";

cout<<"*//";

return 0;

}

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

Введите строку: Привет

длина строки Привет=6

_______Привет

*******Привет

^^^^^^^Привет* Это Я

-ое: Привет,2-ое:Привет* Это Я

-ое меньше

1-ое: Привет* Это Я,2-ое:Привет* Это Я

они равны

-ое: Привет* Это Я,2-ое:Привет

1-ое больше

Символ * находится  на 7позиции


. Виртуальные методы. Полиморфизм.

  1.  Виртуальные методы.

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

 Как работают виртуальные функции.

Если в базовом классе функция объявлена как виртуальная , то её вызовы будут обрабатываться методом динамического связывания. Ключевое слово virtual предписывает компилятору генерировать дополнительную информацию о функциях.

Предположим, что существует класс GrafObj(x,y) —базовый класс, исходный от него класс Cub(d,w). При создании объекта класс Cub, сначала выполниться конструктор базового класса.

на рисунках представлены схемы работы виртуальной функции Draw VPTR — указатель на таблицу виртуальных функций. Эта таблица содержит указатели на все виртуальные функции класса. При инициализации объекта Cub указатель VPTR указывает на Draw(GrafObj), после работы конструктора самого класса Cub, он перенастраивается для функции Draw для класса Cub (если есть на что настраиваться).Особенностью работы с виртуальными функциями является то ,что они работают только при обращении к ним по ссылке или указателю.

#include<iostream.h>

class X

{ public:

virtual double A(double x)

{ return x*x; }

double B(double x)

{ cout<<A(x)<<":2=";

return A(x)/2.; }

};

class Y:public X

{ public:

double A(double x)

{ return x*x*x; }

};

int main(void)

{

Y y;

X x;

cout<<"при x=3."<<endl;

cout<<x.B(3.)<<" X"<<endl;

cout<<y.B(3.)<<" Y"<<endl;

}

результат выполнения  программы:

при x=3.

:2=4.5 X  A::X=x*x;

:2=13.5 Y A::Y=x*x*x

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

#include<iostream.h>

class Mammal

{ public:

Mammal():itsAge(1){}

virtual ~Mammal() {}

virtual void Speak() { cout<< " Млекопитающее что-то сказало!\n";}

protected:

int itsAge;

};

class Dog:public Mammal

{ public:

void Speak(){cout<<"Гафф!\n";}

};

class Cat:public Mammal

{ public:

void Speak(){cout<<"Мурр!\n";}

};

void VFunc(Mammal);

void PFunc(Mammal*);

void RFunc(Mammal&);

int main(void)

{

Mammal *prt=0;

int choice;

while(1)

{

bool fQ=false;

cout<<"1--собака, 2-- кошка, иначе-- млекопитающее, 0-- выход.";

cin>> choice;

switch(choice)

{

case 0: fQ=true;break;

case 1: prt=new Dog; break;

case 2: prt=new Cat; break;

default: prt=new Mammal;

}

if(fQ) break;

cout<<"указатель на класс:  " ; Pfunc(prt);

cout<<"ссылка на класс: "; Rfunc(*prt);

cout<<"переменная класса:  -- "; VFunc(*prt);

}

return 0;

}

void VFunc(Mammal M)

{ MV.Speak(); }

void PFunc(Mammal *pM)

{ pM->Speak(); }

void RFunc(Mammal &rM)

{ rM.Speak(); }

/*  результат работы программы:

--собака, 2-- кошка, иначе-- млекопитающее, 0-- выход. 1

указатель на класс:     -- Гафф!

ссылка на класс:        -- Гафф!

переменная  класса:  -- Млекопитающее что-то сказало!

--собака, 2-- кошка, иначе-- млекопитающее, 0-- выход. 2

указатель на класс:     -- Мурр!

ссылка на класс:        -- Мурр!

переменная  класса:  -- Млекопитающее что-то сказало!

--собака, 2-- кошка, иначе-- млекопитающее, 0-- выход. 3

указатель на класс:     -- Млекопитающее что-то сказало!

ссылка на класс:        -- Млекопитающее что-то сказало!

переменная  класса:  -- Млекопитающее что-то сказало!

--собака, 2-- кошка, иначе-- млекопитающее, 0-- выход. 0

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

Конструктор не может быть виртуальным и не может быть виртуального конструктра-копировщика. Если требуется передать указатель на объект произвольного класса и правильно скопировать его можно использовать виртуальный метод Clone()—создающий и возвращающий копию объекта текущего класса.

Virtual Mammal* Mammal::Clone(){return new Mammal(*this);}

Virtual Mammal* Dog::Clone(){return new Dog(*this);}

Virtual Mammal* Cat::Clone(){return new Cat(*this);}

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

Рекомендуется:

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

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

  1.  Абстрактные классы.

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

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

Virtual тип имя_функции (список параметров) =0;

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

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

Рекомендации:

  •  Используйте абстрактные типы данных для создания для создания общего интерфейса для всех производных классов.
  •  Обязательно замещайте в произвольных классах чистые виртуальные функции.
  •  Объявляйте все функции, которые требуют замещения в произвольном классе как виртуальные.
  •  Не пытайтесь создать объект абстрактного класса.
  1.  
    Статические переменные. Шаблоны функций и классов. Связные списки.
    1.  Статические переменные-члены и статические функции-члены.

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

#include<iostream.h>

class Cat

{

public:

Cat(int age):itsAge(age){HayMenyCats++;}

virtual ~Cat(){HayMenyCats--;}

virtual int GetAge() {return itsAge;}

virtual void SetAge(int age){itsAge=age;}

static int HayMenyCats;

private:

int itsAge;

};

int Cat::HayMenyCats =0;

int main(void)

{

const int MaxCat=5;

int i;

Cat *CatHouse[MaxCat];

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

CatHouse[i]=new Cat(Maxcat-i);

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

 {

 cout<<"сейчас у нас ";

 cout<<Cat::HayMenyCats;

 cout<<" кота.\n";

cout<<"удалим кота, возраст которого ";

cout<<CatHouse[i]->GetAge()<<" года \n";

 delete CatHouse[i];

 CatHouse[i]=0;

 }

return 0;

}

у нас  живёт 4 кота .

удалим кота, возраст которого 4 года

у нас  живёт 3 кота .

удалим кота, возраст которого 3 года

у нас  живёт 2 кота .

удалим кота, возраст которого 2 года

у нас  живёт 1 кота .

удалим кота, возраст которого 1 года

рекомендуется:

  •  Применять статические переменные для совместного использования данных несколькими объектами класса.
  •  Необходимо ограничить доступ к статическим ,объявив их private или protected.
  •  Не используйте статические данные для хранения данных одного объекта. Эти переменные предназначены для обмены данными между объектами класса.

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

#include<iostream.h>

class Cat

{

public:

Cat(int age) :itsAge(age) {HayMenyCats++;}

virtual ~Cat(){HayMenyCats--;}

virtual int GetAge() {return itsAge;}

virtual void SetAge(int age) {itsAge=age;}

static int GetHayMeny()

{ return HayMenyCats;}

private:

int itsAge;

static int HayMenyCats;

};

void Info();

int Cat::HayMenyCats =0;

int main(void)

{

const int MaxCat=4;

int i;

Cat *CatHouse[MaxCat];

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

{

CatHouse[i]=new Cat(MaxCat-i);

Info();

cout<<CatHouse[i]->GetAge()<<" года\n";

}

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

{  cout<<"удаляем старшего кота \n"<<endl;

 delete CatHouse[i];

CatHouse[i]=0;

Info();

}

return 0;

}

void Info()

{

cout<< "у нас  живёт "<< Cat::GetHayMeny()<<" кота .\n";

}

Вызов статических функций может быть двух видов:

  •  Cat::GetHayMeny() —без определения объекта, но с указанием класса.
  •  TheCat. GetHayMeny() —с определением объекта, и без указания класса.

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

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

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

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

На пример список. Список может быть для чего угодно.

Построение шаблонов рассмотрим на примере. Необходимо написать функцию расчёта куба для аргумента. (Cub()). 

#include<iostream.h>

#include<math.h>

template <class T>

T cub(T x)

{ return x*x*x;}

template <class SwapType>

void Swap(SwapType &x,SwapType &y)

{ SwapType tmp;

tmp=x;

x=y;

y=tmp;

}

template <class T1,class T2>

T1 max(T1 x,T2 y)

{

if(x>y)return x;

else return y;

}

void main(void)

{

int i=3;

float f=3.12;

double x=3.1e2;

cout<<"//целое "<<cub(i)<<endl;

 cout<<"//вещественное "<<cub(f)<<endl;

cout<<"//двойной точности "<<cub(x)<<endl;

 char ch1='a',ch2='z';

cout<<"//1-- "<<ch1<<"  2-- "<<ch2<<endl;

Swap(ch1,ch2); cout<<"//переставили "<<endl;

cout<<"//1-- "<<ch1<<"  2-- "<<ch2<<endl;

int c1=0,c2=9;

cout<<"//1-- "<<c1<<"  2-- "<<c2<<endl;

 Swap(c1,c2); cout<<"//переставили "<<endl;

 cout<<"//1-- "<<c1<<"  2-- "<<c2<<endl;

cout<<"//max из  int "<<c1<<" и int "<<c2<<" = "<<max(c1,c2)<<endl;

cout<<"//max из  char "<<ch1<<" и char "<<ch2<<" = "<<max(ch1,ch2)<<endl;

cout<<"//max из  int "<<i<<" и float "<<f<<" = "<<max(f,i)<<endl;

}

//целое 27

//вещественное 30.3713

//двойной точности 2.9791e+07

//1-- a  2-- z

//переставили

//1-- z  2-- a

//1-- 0  2-- 9

//переставили

//1-- 9  2-- 0

//max из  int 9 и int 0 = 9

//max из  char z и char a = z

//max из  int 3 и float 3.12 = 3.12

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

Шаблон класса представляет собой  скелет обобщённого класса. Ситаксис такой:

Template< список_аргументов_шаблона>

{

 //тело класса

};

Каждый аргумент в списке является либо объявлением типа float a; либо идентификатором класса class T. Из-за этого определение функции-метода шаблонного класса имеет вид:

Template< список_аргументов_шаблона>

Тип_результата имя_класса < список_аргументов_шаблона>::

Имя_функции( список_аргументов_функции)

{

//тело функции.

}

объявление объекта шаблонногокласса:

имя_класса_шаблона < список_аргументов_шаблона> имя_объекта;

Пример использования шаблонов будем рассматривать на создании очереди.

  1.  Связные списки и другие структуры.

Для работы с массивом необходимо знать его размерность, хотя бы в начале работы программы. Если это данное не известно то можно либо сильно увеличить резервируемую память, либо рисковать ограниченность пространства для данных в массиве. Данную проблему решают связные списки.  Связанный список это структура данных, содержащая из взаимосвязанных блоков. Идея состоит в том, что бы создать класс, который поддерживал бы данные определённого типа, среди которых был бы указатель, связанный с другим объектом этого класса.

Существуют три основных вида списков:

  •  Однонаправленные списки  (очередь, стек);
  •  Двунаправленные списки (дек и т.д.);
  •  Деревья.

Названия образуются от количества связующих список указателей в каждом объекте списка.

Список состоит из узлов, узлы представляют собой абстрактные классы. Начало —это начало обработки промежуточных узлов списка, которые отвечают за хранение данных, указатель узла - “хвост” обязательно имеет значение null (0).

// программа работы с динамическими списками при помощи классов.

// однонаправленные списки, двунаправленные списки и деревья.

#include <iostream.h>

#include <iomanip.h>

#include <fstream.h>

ofstream fout("rez.txt");

ifstream fin("rez.txt");

class Data

{

public:

Data ();

~Data(){delete fam;}

int Compare(const Data &);

void Show();

private:

char fam[25];

float ball;

};

Data::Data()

{

cout<<"\n Введите фамилию и средний балл :";

 cin>>fam>>ball;

}

void Data::Show()

{ cout.width(25);

cout.fill(' ');

cout<<fam;

cout.width(10);

cout.fill('-');

cout.precision(3);

cout<<ball<<"\n";

fout.width(25);

fout.fill(' ');

fout<<fam;

fout.width(10);

fout.fill('-');

fout.precision(3);

fout<<ball<<"\n";

//fout<<fam<<"--"<<ball<<endl;

}

int Data::Compare(const Data &theO)

{

if(strcmp(fam,theO.fam)<0)return -1;

if(strcmp(fam,theO.fam)==0)return 0;

if(strcmp(fam,theO.fam)>0)return 1;

}

class Node;

class HeadNode;

class TailNode;

class InternalNode;

class Node

{public:

Node(){}

virtual ~Node(){}

virtual Node* Insert(Data* theData)=0;

virtual void Show()=0;

private:

};

class InternalNode:public Node

{public:

InternalNode(Data *theData, Node *next) ;

~InternalNode(){delete myNext,delete myData;}

virtual Node* Insert(Data *theData);

virtual void Show() {myData->Show();myNext->Show();}

private:

Data* myData;

Node* myNext;

};

InternalNode::InternalNode(Data *theData,Node*next):

myData(theData),myNext(next)

{}

Node* InternalNode ::Insert(Data *theData)

{

int result=myData->Compare(*theData);

switch(result)

{

case  0:

case1:{InternalNode *dataN=new InternalNode (theData,this);

return dataN;}

case-1:{myNext=myNext>Insert(theData);

return this;}

 }

return this;

}

class TailNode:public Node

{

public:

TailNode(){}

~TailNode() {}

virtual Node* Insert(Data *theData);

virtual void Show(){ }

private:

};

Node* TailNode::Insert(Data *theData)

{

InternalNode *dataN=new InternalNode (theData,this);

return dataN;

}

class HeadNode:public Node

{public:

HeadNode(){myNext=new TailNode;};

~HeadNode(){delete myNext;}

virtual Node* Insert(Data *theData);

virtual void HeadNode::Show();

private:

Node* myNext;

};

void HeadNode::Show()

{

cout<<"        Фамилия         "<<"   балл  "<<endl;

fout<<"        Фамилия         "<<"   балл  "<<endl;

 myNext->Show();

}

Node* HeadNode::Insert(Data *theData)

{

myNext=myNext>Insert(theData);

return this;

}

class Admin

{public:

Admin();

~Admin(){delete myHead;}

void Insert(Data* pData);

void ShowAll(){myHead->Show();}

private:

HeadNode* myHead;

};

Admin::Admin()

{

myHead=new HeadNode;

}

void Admin:: Insert(Data*pData)

{

myHead->Insert(pData);

}

int main()

{

Data* pData;

int val=1;

Admin ll;

while(val!=0)

{

 pData=new Data();

 

 ll.Insert(pData);

cout<<"хотите закончить ввод? Если да, то введите 0";

 cin >>val;

}

ll.ShowAll();

cout<<"********************************\n";

fout.close();

 return 0;

}


 

А также другие работы, которые могут Вас заинтересовать

80000. Интеллектуальный анализ влияния текущих событий на протекание связанных с ними процессов 336.53 KB
  Дипломная работа посвящена методике разработки программного продукта для поиска причин в изменениях трендов в данных. Рассмотрено создание системы предобработки данных и разработка системы классификации на базе различных алгоритмов машинного обучения. В работе определяется область применения разработанной программы. Для разработки системы предобработки данных использован язык программирования
80001. ОРГАНІЗАЦІЯ ОБЛІКУ ВИТРАТ ТА МЕТОДИКА АНАЛІЗУ СОБІВАРТОСТІ ПОСЛУГ 757 KB
  Привести існуючий порядок обліку затрат до Методичних рекомендацій з формування собівартості будівельно-монтажних робіт; запровадити зарубіжний досвід системи калькулювання «Директ-костинг»; автоматизувати облік, що підвищить продуктивність праці бухгалтерів;
80002. История поселка Хвойная 234 KB
  Хвойнинский район находится на северо-западе Европейской части России, на стыке трех областей: Ленинградской, Вологодской и Новгородской. Он граничит на Севере с Тихвинским районом Ленинградской области, на северо-востоке с Чагодощенским районом Вологодской области и районами Пестовским, Мошенским, Боровичским, Любытинским Новгородской области
80003. Задачи IV соросовской олимпиады по математике для 6 - 11 классов 1.94 MB
  В последнее десятилетие широкую известность получили так называемые соросовские олимпиады, проводимые под эгидой фонда Сороса. Уровень этих олимпиад весьма высок и успех на них возможен только при наличии незаурядных математических способностей.
80004. ВЛИЯНИЕ НЕСТАЦИОНАРНЫХ ЭФФЕКТОВ НА ДИНАМИКУ ВСПЛЫТИЯ ПУЗЫРЬКА 2.21 MB
  Данная работа состоит из трех разделов. В первом рассмотрена динамика всплытия пузырька в стационарном режиме. Приведены теоретические расчеты скорости пузырьков в различных растворах. При движении пузырьков в режиме ускорения на них действуют дополнительные силы: сила, приведенной массы, связанная с присоединенной массой и сила Бассэ.
80005. Сравнительный анализ «опыта потока» в игровой и продуктивной деятельности 1.15 MB
  Человек, переживающий поток, оказывается сверхвовлеченным и сверхсконцентрированным в своей деятельности, причем она доставляет ему огромное удовольствие. Поток принадлежит к кругу явлений внутренней мотивации: деятельностью, в которой возникает поток, люди продолжают заниматься ради самого процесса, конечный результат не столь важен для них.
80006. ОПТИМИЗАЦИЯ ПЛАНА РЕГЛАМЕНТНЫХ РАБОТ ПО КРИТЕРИЮ МАКСИМУМА СРЕДНЕГО ПОТОКА В СЕТИ ПРИМЕНИТЕЛЬНО К ЗАДАЧЕ ТРАНСПОРТИРОВКИ НЕФТИ ПО МАГИСТРАЛЬНОМУ НЕФТЕПРОВОДУ 960 KB
  проведена программная реализация алгоритма Форда – Фалкерсона нахождения максимального потока в сети, построен и программно реализован алгоритм субоптимального планирования регламентных работ на участках нефтепровода по критерию максимума потока в сети. Тем самым разработан и реализован метод решения задачи максимизации потока в нестационарной сети на основе алгоритма Форда – Фалкерсона.
80007. Анализ и моделирование расщепления ДНК ультразвуком 4.97 MB
  Количественный анализ экспериментальных данных по расщеплению молекул ДНК ультразвуком и развитие подходов к моделированию реакции ДНК на внешние воздействия. Такие подходы используются для решения задачи о физической интерпретации специфичности расщепления молекул ДНК ультразвуком.