void step(float, int); // прототип функции
main( )
{
int i;
float m, s;
clrscr(); // очистка экрана
cout<<"\nВведите m = ";
cin>> m;
cout<<"\nВведите i = ";
cin>> i; step(m, i);
cout<<"\nНажмите любую клавишу…";
getch();
}
void step(float base, int k) // заголовок функции
{
int i; float p = 1;
for(i=1;i<=k;++i)
p*=base;
cout<<"\n"<< base << " в степени "<< k << "
равно "<
#include
int multiply(int, int &);
main( )
{
clrscr(); //очистка экрана
int x1,y1,z;
cout<<"\nВведите первый
сомножитель=; cin>>x1;
cout<<"\nВведите второй
сомножитель="; cin>>y1;
cout<<"\nРезультат ="<
#include
int multiply(int ,int *); // прототип функции
main()
81
{
clrscr(); //очистка экрана
int x1,y1,z;
cout<<"\n Введите первый сомножитель=";
cin>>x1;
cout<<"\nВведите второй сомножитель=";
cin>>y1;
z=myltip(x1, &y1); // вызов функции
cout<<"\nРезультат ="<< z;
cout<<"\n -------------------------------- \n";
cout«"\nНажмите любую клавишу…";
getch();
}
int multip(int x, int *y) // заголовок функции
{
return *y*x; // изменение значения параметра
}
Рис. 9.5
10. Указатели
10.1. Назначение указателей
Указатели позволяют применять язык C++ в самом широком диапазоне задач - от драйверов устройств на уровне аппаратного обеспечения и управляющих
систем реального времени до операционных систем и компиляторов, анимации и
мультимедийных приложений. C++ - это идеальный инструмент для решения задач в поразительно широком многообразии прикладных областей.
Указатель - это особый тип переменной, содержащей в памяти адрес того
элемента, на который он указывает. При этом имя элемента отсылает к его значению прямо, а указатель косвенно. Поэтому ссылка на значение посредством указателей называется косвенной адресацией. Смысл использования указателей состоит в том, что, отводя место только для запоминания адреса в памяти, Вы получаете идентификатор (переменную типа указатель), который может ссылаться на
любой элемент в памяти указанного типа, причем в разный момент указатель мо-
82
жет указывать на разные элементы, т.е. не надо перемещать в памяти с места на
место кучи байтов, достаточно просто изменить указатель.
Указатель может указывать на любые объекты: переменные, массивы, классы, структуры, и даже на функции. При объявлении указателя необходимо указать
тип элемента, на который он будет указывать. Сам указатель занимает ровно
столько места, сколько требуется для хранения адреса элемента. Признаком указателя является символ (*), который означает, что следующая за ним переменная
является указателем. Рассмотрим примеры объявления указателей для различных
типов переменных.
int *r; float*f; char*ch; long *g[10]; long (*t)[10]; int(*fun)(double, int );
указатель на целое число, указатель на
действительное число, указатель на
символьную переменную, массив из
10указателей на длинное целое, указатель на
массив из десяти длинных целых,
указатель на функцию с именем fun.
Для инициализации указателей используется операция присваивания "=".
Присваивание начального значения означает, что при определении указателя ему
следует присвоить либо адрес объекта, либо адрес конкретного места в памяти,
либо число 0. Примеры каждого типа инициализации выглядят так:
1. Присваивание указателю адреса существующего объекта:
С помощью операции получения адреса:
int a=5;
// целая переменная
int*p = &a;
// указателю присваивается адрес переменной а
int*p (&a);
// то же самое другим способом
С помощью значения другого инициализированного указателя:
int*r = p;
С помощью имени массива или функции, которые трактуются как
адрес:
int b[10];
int *t = b;
// массив
// присвоение адреса начала массива
2. Присваивание указателю адреса области памяти в явном виде:
char*z = (char*)0x00000417;
Здесь 0x00000417 - шестнадцатеричная константа ,
(char*) - операция приведения константы к типу указатель на char.
83
3. Присвоение пустого значения:
int*p = 0;
Поскольку гарантируется, что объектов с нулевым адресом нет, пустой указатель можно использовать для проверки, с помощью которой можно оценить:
ссылается указатель на конкретный объект или нет.
4. Выделение участка динамической памяти и присвоение ее адреса указа-
телю с помощью операции new:
• int*n = new int;
// 1
• int*m = new int(10); // 2
• int*q = new int[10]; // 3
В операторе 1 операция new выполняет выделение достаточного для размещения величины типа int участка динамической памяти и записывает адрес начала
этого участка в переменную n. В операторе 2, кроме того, производится инициализация выделенной памяти значением 10. В операторе 3 операция new выделяет
память под массив из 10 элементов и записывает адрес начала этого участка в переменную q, которая может трактоваться как имя массива.
10.2. Операции над указателями
С указателями связаны два специальных оператора: & и*. Обе эти операции
унарные, т.е. имеют один операнд, перед которым они ставятся. Операция & соответствует действию "взять адрес". Операция * соответствует словам "значение,
расположенное по указанному адресу". Например:
int y = 5;
int *py;
py = &y; .
Здесь оператор py = &y; присваивает адрес переменной у указателю py. Говорят, что переменная py указывает на y. Оператор * обычно называют оператором косвенной адресации, или операцией разыменования, возвращающей значение объекта (элемента), на который указывает ее операнд (т.е. указатель). Например, оператор cout << *py <<"\n"; печатает значение переменной у, а именно 5.
84
Использование * указанным способом позволяет обеспечить доступ к величине,
адрес которой хранится в указателе.
На рис. 10.1 приведен листинг программы, в которой рассматриваются примеры использования операций & и *.
#include
#include
main()
{
int a;
// а - целое число
int *pa ;
// pa - указывает на адрес целого числа
clrscr();
a = 7;
pa = &a;
// pa устанавливаем равным адресу переменной а
cout<<"\nАдрес а:"<<&a<<"\n"<<"Значение ра:"<
#include
main()
{
int x; // x - целое число
int *p ,*p1; // указывают на целые числа
clrscr(); // очистка экрана
p = &x; // указателю присваивается адрес целого числа х
p1=p+3; cout<<"\nНачальное значение р:"< = < p >
+ n*< количество байт памяти базового типа указателя>. Можно так же вычитать
один указатель из другого. Так, если р и р1 - указатели на элементы одного и того
же массива, то операция р - р1 дает такой же результат, как и вычитание соответствующих индексов массива. Указатели можно сравнивать, при этом применимы
все 6 операций:
<, >, <=, >=, =, ==, !=.
Сравнение p
void main()
{
int a=1, b=2;
int *c=&b, *d=&a;
cout<< a <<' '<< b<<' '<<*c«' '<< d<<' '<< c<<' '<< *d<<' '<< "\n";
}_________________________________________________________________
Результаты работы программы:
a=1, b=2, *c=2, d=0xfff2, c=0xfff4, *d=1.
Рис. 10.3
11. Массивы
Массивы - это группа элементов одинакового типа (double, float, int и т.п.),
хранящихся под одним именем. Массив характеризуется своим именем, типом
хранимых элементов, размером (количеством элементов), нумерацией элементов
и размерностью. Различают одномерные и многомерные массивы. Основная форма объявления массива размерности N имеет следующий формат:
тип < имя массива> [размер 1][размер2]... [размер N ];
тип - базовый тип элементов массива,
[размер1][размер2]... [ размер N] - количество элементов одномерных массивов,
входящих в многомерный массив.
11.1. Одномерные массивы
Чаще всего используются одномерные массивы, форма объявления которых
будет иметь вид Тип <имя массива> [размер]. Например, оператор int A[10]; объявляет массив с именем А, содержащий 10 целых чисел. Доступ к элементам массива осуществляется выражением А[i] , где i - индекс элементов массива, который
начинается с нуля и в данном примере заканчивается цифрой 9. Поэтому элемент
А[0] характеризует значение первого элемента массива, А[1] - второго, А[9] - по-
следнего. Объявление массива можно совмещать с заданием элементам массива
начальных значений. Эти значения перечисляются в списке инициализации после
знака равенства, Темаяются запятыми и заключаются в фигурные скобки,
например: int A[10] = {1,2,3,4,5,6,7,8,9,10};. Элементы массива могут иметь любой
88
тип. Так, например, оператор char S[10]; объявляет массив из символов. Массив
символов - это фактически строка, и число символов, помещаемых в строку,
должно быть на единицу меньше объявленного размера массива. Это обусловлено
тем, что строка кончается нулевым символом и будет, к примеру, иметь вид char
S[10] = {"abcdefghi\0"};. Нулевой символ в конце можно не указывать, поэтому
нормально будет воспринято такое объявление:
char S[10] = {"abcdefghi"};.
11.2. Многомерные массивы
Многомерным называется массив, элементами которого являются одномерные массивы. Например, двумерный массив может быть объявлен таким образом:
int A2[10][3];. Этот оператор описывает двумерный массив, который можно пред-
ставить себе как таблицу, состоящую из 10 строк и 3 столбцов. Доступ к значениям элементов многомерного массива обеспечивается через индексы, каждый из
которых заключается в квадратные скобки. Например, A2[3][2] - значение элемента, лежащего на пересечении четвёртой строки и третьего столбца (напоминаем, что индексы начинаются с 0). Если многомерный массив инициализируется
при его объявлении, список значений по каждой размерности заключается в фигурные скобки. Приведённый ниже оператор объявляет и инициализирует двумерный массив A2 размерностью 3 на 5:
int A2[3][5] = {{1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15}};,
однако допустим и упрощенный способ инициализации:
int A2[3][5] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};.
11.3. Примеры использования массивов
Так как массив является структурным типом, то основные операции с ним
необходимо проводить с помощью оператора цикла. Покажем это на ряде примеров. В примере на рис. 11.1 с помощью оператора цикла осуществляется присвоение начальных нулевых значений элементам массива n[5], содержащего пять целых чисел. Печать массива осуществляется в табулированном формате. Первый
оператор вывода печатает на экране заголовки столбцов, а второй выводит эле89
менты массива и их значения. Функция setw() указывает ширину поля, в котором
будет выведено следующее значение.
#include
#include
#include
#include
main( )
{
clrscr();
// очистка экрана
int n[5];
for(int i=0;i<5;i++)n[i]=0;
// обнуление элементов массива
cout<<"элемент"«setw(13)<<"значение\n"; // вывод
заголовков for(i=0;i<5;i++)
cout<
#include
#include
#include
main()
{
clrscr(); int i;
90
int mes[12]={31,28,31,30,31,30,31,31,30,31,30,31}; // инициализация массива int mcopy[12];
for(i=0;i<12;i++) mcopy[i]=mes[i]; // cout<<"Исходный
массив "<
#include
#include
main()
{
float a=0;
float y[10], x[10];
int i=0;
clrscr();
cout<<"\nВведите a : ";
cin>>a;
for( i=0; i<10;i++)
{
cout<<"\nВведите x["<>x[i];
y[i]= (x[i] + a) / sqrt((x[i]*x[i])+1);
cout<<"\t\t\t y["<
#include
#include
main()
{
float a=0;
float y[3][2], x[3][2]; int i=0, j=0;
clrscr();
cout<<"\nВведите a : ";
cin>> a; // ввод переменной а for( i=0; i<3; i++)
{
for( j=0; j<2; j++)
{
cout<<"\nВведите x["<> x[i][j]; // ввод элементов массива x[i][j] y[i][j]=(x[i][j] + a)/sqrt((x[i][j] *
x[i][j])+1);
cout<<"\t\t\t y["<
#include
#include
main()
{
float a[10];
int i=0, n=0, p=0, zero=0; // обнуление переменных clrscr();
cout<<"\nОпределить количество положительных и отрицательных
элементов массива a[10]\n";
for( i=0; i<10;i++)
{
cout«"\nВведите a["<>a[i];
}
for( i=0; i<10; i++)
{
if( a[i] > 0) p += 1; // определение количества положительных .элементов
if( a[i] < 0) n += 1; // определение количества отрицательных элементов
if( a[i] == 0) zero += 1; // определение количества нулевых элементов
}
cout<<"\n\n";
cout<<"\nЧисло положительных элементов
="<
#include
#include
#define row 2 // строки
#define col 3 // столбцы
main()
{
float b[row][col];
// объявление массива
int i=0, j=0,n=0, p=0, zero=0;
clrscr();
cout<<"\nОпределить количество положительных и отрицательных элементов";
cout<<"\n массива b["<> b[i][j];
// ввод элементов двумерного массива
}
cout<<"\n\n";
}
for(i=0; i 0) p += 1;
if(b[i][j] < 0) n += 1;
if(b[i][j] == 0) zero += 1;
}
}
cout<<"\nЧисло положительных элементов = "<
#include
#include
#include
#define n 5
vec(int [ ], int); // прототип функции вычисления min компоненты вектора
main()
{
int y[n], i, min;
clrscr();
for(i=0;i>y[i];
// ввод элементов вектора
}
min=vec(y, n); // вызывающая функция cout<<"\nМинимальная
компонента = "<
#include
#include
#include
#define n 2
#define m 3
mas(int [ ][m], int, int);
main( )
{
int y[n][m], i, j, min ;
clrscr();
for(i=0;i>y[i][j];
// ввод массива
}
min=mas(y, n, m);
// обращение к функции и получение результата
cout<<"\n\nМинимальная компонента = "<
#include
void main()
{ clrscr();
int a[5], sum = 0, *p;
int kol = 0, i;
p = &a[0]; // инициализация указателя адресом первого элемента cout << "
Ввод данных в массив a[ ]\n";
for ( i = 0; i <5; i++){
97
cout << " a [ " << i << " ] = ";
cin >> *(p+i);
// разыменовывание смещенного указателя
}
// расчет суммы и количества положительных элементов
for ( i = 0; i < 5; i ++)
if ( *(p+ i) > 0 ) {
sum += *( p+i );
kol ++;
}
// вывод исходных данных и результатов
cout << "\n\n\n Элемент массива Адрес элемента массива \n";
for ( i = 0; i < 5; i++ ){
cout << *( p+ i) << "\t\t " << (p+i) << "\n";
// вывод результатов
}
cout << "\nсумма = " << sum << "^количество = " << kol; cout<<"\n\n";
cout<<"\nНажмите любую клавишу…"; getch();
} _________________________________________________________________________
Результаты работы программы:
Ввод данных в массив a[ ] :
a[0]=1
a[1]=2
a[2]=5
a[3]=5
a[4]=4
Элемент массива
1
2
3
4
5
Адрес элемента массива
Oxffec
Oxffee
Oxfff0
Oxfff2
Oxfff4
сумма = 17
количество=5
Рис. 11.9.
На рис.11.10 приведен пример программы, в которой с помощью указателей
формируется двумерный массив а[2][2], а из минимальных элементов его столбцов формируется массив b[2]. Значения полученных массивов выводятся на дисплей.
#include
#define I 2
98
#define J 2
#include
void main()
{
clrscr();
int a[I][J], b[J], min, *p ;
int i,j;
p = &a[0][0];
// инициализация указателя адресом первой ячейки
cout << "Введите данные в массив a["<< I <<"]["<> *(p + i*I + j);
// ввод массива
}
// расчет массива b[2]
for ( j = 0; j < J; j++ ) {
// цикл по столбцам
min = *(p + j); // присваивание min значения первого элемента столбца
for (i = 1; i < I; i+)
// цикл по строкам, начиная со второго элемента
if ( ( *(p + i*I + j)) < min) min = *(p + i*I +
j); *(b + j) = min;
}
cout << "nВывод исходного массива a[,]:";
for (i = 0; i < I;i++){
cout << "\n";
for ( j = 0; j < J;j++){
cout <<"\t"<< *(p + i*I + j);
}
}
cout << "\n\nВывод полученного массива
b[]:\n"; for ( j = 0; j < J; j++ ){
cout <<"\t"<< *( b + j);
}
cout<<"\n\n";
cout<<"\nНажмите любую клавишу…";
getch();
}____________________________________________________________
Результаты работы программы:
Введите данные в массив a[2][2]:
a[0][0]=1
a[0][1]=4
a[1][0]=5
a[1][1]=3
99
Вывод исходного массива a[2][2]:
1
5
4
3
Вывод полученного массива b[]:
1
3
Рис. 11.10.
12.Форматирование ввода-вывода
12.1. Форматированный ввод-вывод
В языке С++ задача форматирования решается с помощью манипуляторов
потока.
Манипуляторами называются функции, которые можно включать в цепочку
операций ввода и вывода для форматирования данных.
Они позволяют выполнять следующие операции: задание ширины полей,
задание точности, установку и сброс флагов формата и т. д. Манипуляторы потоков dec, oct и hex задают основания чисел. Чтобы установить шестнадцатеричный
формат представления элементов данных (с основанием 16), необходимо в потоке
использовать манипулятор hex. Манипулятор oct используется для установки
восьмеричного формата представления данных, а манипулятор dec осуществляет
возврат представления данных к десятичному формату. Установленный формат
потока сохраняется до тех пор, пока он не будет изменен явным образом. В программе на рис. 12.1 показано использование манипуляторов потока
dec , oct и hex.
// Пример программы на использование манипуляторов потока
#include
#include
main()
{
int n;
clrscr();
cout<<" Введите десятичное число"<<" ";
cin>>n;
cout <
#include
#include
#include
main()
{
int d;
double root2 = sqrt(2.0);
clrscr();
cout<<" корень квадратный из 2 с точностью d от 0 до 9"<
#include
#include
main()
{
int n=25;
clrscr();
for(int i=0; i< 5; i++)
cout<
#include
#include
main()
{
int n=25;
clrscr();
for(int i=0; i< 5; i++){
cout.width(i);
103
cout <
#include
const int size = 80;
main()
{
clrscr();
char buffer[size] ;
cout << "Введите предложение :"<
#include
void main()
{
clrscr();
cout << "Введите имя файла для ввода: ";
7. char fname[80]; //создание массива для записи имени файла
8. cin >> fname;
9. ofstream fout(fname);
// открыть файл для записи
10. if (!fout)
// проверить открытие файла
11. {
12. cout << "Невозможно открыть файл";
13. return;
14. }
15. char c;
16. cout << "Вводите информацию :\n";
17. cout << "Конец ввода - символ *\n";
18. while ( fout )
19. {
20. fout.put(c); // запись информации в файл
21. cin.get(c);
22. if ( c == '*')
23. break;
24. }
25. fout.close();
26. cout << "Введите имя файла для считывания : ";
27. cin >> fname;
28. ifstream fin (fname);
29. if ( !fin)
30. {
31. cout << "Невозможно открыть файл";
32. return;
33. }
34. while ( fin )
35. {
36. fin.get(c); // считывание информации с файла
110
37.
38.
39.
40.
41.
42.
cout << c;
}
fin.close();
cout<<"\nНажмите любую клавишу... ";
getch();
}
Рис. 14.1.
В строке 7 создается массив для записи имени файла, а в строке 8 предлагается ввести имя файла, которое записывается в массив fname. В строке 9 создается
объект ofstream c именем fout, который связывается с введенным ранее именем
файла. В строках 10-12 осуществляется проверка открытия файла. При неудачной
попытке выводится сообщение: "Невозможно открыть файл". В строках 14-23
осуществляется запись вводимой информации в файл и контроль конца записи.
Запись информации осуществляется с помощью функции put(c), которая посимвольно вводит данные в файл. Контроль конца записи реализуется с помощью
функции get(c), которая считывает вводимые в файл данные и заносит их в символьную переменную с. Ввод информации в файл продолжается до тех пор, пока в
переменной с не появится ключевой символ '*', определяющий конец записи. В
строке 25 файл закрывается. В строке 28, после введения имени в строке 26, файл
открывается заново, но в этот раз для чтения, и его содержимое посимвольно выводится в программу в строках 34-37. В строке 39 файл закрывается окончательно.
Другой пример создания файла последовательного доступа показан на рис.
14.2. В этом примере для каждого клиента программа получает номер счета, имя
клиента и сумму денег, которую клиент должен компании за товары и услуги, полученные в прошлом. Данные, полученные от каждого клиента, образуют записи,
в которых номер счета используется в качестве ключа записи. Таким образом,
файл будет создаваться, и обрабатываться, в соответствии с номером счета. В
программе файл должен быть открыт для записи, так что создается объект класса
ofstream. Конструктору объекта передаются два аргумента - имя файла и режим
открытия файла. Для объекта ofstream режим открытия файла может быть или
111
ios::out - для записи данных в файл, или ios::app - для добавления данных в конец
файла.
#include
#include
#include
void outputLine(int,char*,float) ;
void main()
{
clrscr();
cout << "Введите имя файла для ввода: ";
char fname[80]; // создание массива для записи имени файла cin
>> fname;
ofstream fout(fname); //открыть файл для записи
if (!fout) { // проверить открытие файла
cerr << "Невозможно открыть файл"<>accout >>name>>sum) {
fout<< accout<<" " <> accout>>name>>sum) {
outputLine(accout, name, sum); // запись информации в файл
}
fin.close();
cout<<"\nНажмите любую клавишу...";
getch();
}
void outputLine(int acct, char*name, float bal){
cout<>accout >>name>>sum) вводит каждый набор данных и
определяет, не введен ли признак конца файла. Когда достигнут признак конца
файла или вводятся неверные данные, условное выражение возвращает 0 и оператор while завершает свою работу. После этого файл закрывается явным образом с
помощью функции-элемента clouse: fout.close();.
Для чтения файла последовательного доступа его открывают путем создания объекта класса ifstream. Объекту передаются два аргумента - имя файла и режим открытия файла. Объявление
ifstream fin ("clients.dat", ios::in);
создаёт объект fin класса ifstream, связанный с файлом clients.dat, который открывается для чтения. По умолчанию объекты класса ifstream открыты для чтения,
поэтому для открытия файла clients.dat для чтения может быть использован оператор ifstream fin ("clients.dat"); .
Строка while (fin>>accout >>name>>sum) читает из файла набор данных,
т.е. записи. Всякий раз, когда выполняется оператор while, считывается следующая запись из файла. Записи выводятся на экран с помощью функции output(), которая использует параметризованные манипуляторы потока для форматирования
данных, изображаемых на экране. Когда достигается конец файла, входная последовательность в операторе while возвращает 0, и программа выходит из цикла.
Файл закрывается оператором close(), и программа завершает свою работу.
При поиске данных в последовательном файле программа начинает чтение
данных с начала файла и читает все данные последовательно до тех пор, пока не
будут найдены требуемые данные. Это приводит к необходимости обрабатывать
файл последовательно несколько раз (каждый раз с начала), если искомые данные
расположены в разных местах файла. Классы istream и оstream содержат функ114
ции-элементы для позиционирования, которые определяют порядковый номер
следующего байта в файле для считывания или записи. Этими функциямиэлементами являются seekg (позиционировать при извлечении из потока) для
класса istream и seekp (позиционировать при помещении в поток) для класса
оstream. Кроме того, любой объект класса istream имеет указатель get, показыва-
ющий номер очередного вводимого в файл байта, а любой объект класса оstream
имеет указатель set, показывающий номер очередного выводимого из файла байта. Оператор fin.seekg(0); позиционирует указатель на начало файла. Аргумент
функции seekg( ) обычно является целым типа long, а второй аргумент, который
может быть задан, показывает направление позиционирования. Оно может быть
ios::beg при позиционировании относительно начала потока (используется по
умолчанию), ios::cur - для позиционирования относительно текущей позиции и
ios::end - для позиционирования относительно конца потока.
Примеры:
// Позиционировать fileObject на n-й байт(полагаем ios::beg)
fileObject . seekg(n );
// Позиционировать fileObject на n байтов вперед
fileObject . seekg(n , ios::cur );
// Позиционировать fileObject на i-й байт от конца файла
fileObject . seekg(n , ios::end);
// Позиционировать fileObject на конец файла
fileObject.seekg(0 , ios::end);
Те же самые операции могут быть выполнены с помощью функцииэлемента seekp класса оstream. Функции-элементы tellg и tellp возвращают текущие позиции указателей соответственно get и set. Следующий оператор присваивает переменной location = fileObject.tellg(); значение указателя get.
Программа на рис. 14.3 позволяет менеджеру по кредитам отображать на
экране информацию о клиентах с нулевой задолженностью, информацию о клиентах-задолжниках и клиентах, которым должна компания. Программа отображает меню и позволяет по каждой из трех опций получить соответствующую информацию по кредитам.
115
// Программа запроса кредитной информации
#include
#include
#include
#include
#include
void output( int , char* ,float )
; int main()
{
clrscr();
ifstream fin("Clients"); //открытие файла для записи
if(!fin){
cerr<<" файл не открыть";
exit(1);
}
cout<<"Запрос на ввод \n"
<<"1-список счетов с нулевым балансом\n"
<<"2- список счетов c кредитным балансом\n"
<<"3- список счетов с дебитовым сальдо\n"
<<"4 -конец счета \n";
int zapros; cin
>>zapros;
while(zapros !=4){
int account; char name[10];
float sum;
fin>>account>>name>>sum;
switch( zapros){
case 1:
cout<>account>>name>>sum;
}
break;
case 2:
cout<>account>>name>>sum;
}
break;
case 3:
116
cout< 0)
output(account, name, sum );
fin>>account>>name>>sum;
}
break;
}
fin.clear();
fin.seekg(0);
cout<> zapros ;
}
cout<<"\nНажмите любую клавишу…";
getch();
return 0;
}
void output(int a, char* n, float s)
{
cout<
#include
#include
#include
#include
void output(int, char*, float); //прототип функции ввода
struct Vkladchik { // определение структуры "Вкладчик"
int account; // номер счета
char name[10]; // имя
float suma;
// сумма вклада
};
int main()
{
clrscr();
Vkladchik k ; // создание экземпляра объекта
cout<<"Введите счет, имя , сумму \n";
cin>>k.account>>k.name>>k.suma;
cout<<"Счет"«setw(9 )<<"Имя" <
#include
#include
#include
#include
void output(int , char* ,float ) ;
struct Vkladchik {
// определение структуры "Вкладчик"
int account;
// номер счета
char name[10]; // имя
float suma;
// сумма вклада
};
int main()
{ int i, n;
clrscr();
cout<<"Введите число записей n=
"; cin>>n;
Vkladchik k[10]; // создание массива экземпляров объектов
cout<<"Введите счет, имя, сумму \n"; for(i=0;i>k[i].account>>k[i].name>>k[i].suma;
cout<<"?"<
#include
#include
#include
#include
void output(int, char*, char*, int ); // прототип функции вывода
struct book{
// определение структуры
int nzap;
// номер записи
char fam[20]; // фамилия
char dol[10]; // должность
int otdel;
// отдел
};
int main(){
clrscr();
book b;
// создание экземпляра объекта
ofstream outfile("kniga");
//открытие файла для записи
if(!outfile){
cerr<<"файл не открыть";
exit(1);
}
122
cout<<"Введите номер записи, фамилию, должность, номер отдела"
<<" и символ eof по окончании ввода\n";
while(cin>>b.nzap>>b.fam>>b.dol>>b.otdel){
outfile«b.nzap<<" "<>b.nzap>>b.fam>>b.dol>>b.otdel)
output(b.nzap, b.fam, b.dol, b.otdel); // вызов функции вывода
infile.close();
cout<<"\n\n";
cout<<"Нажмите любую
клавишу…"; getch();
return 0;
}
void output(int z, char* a, char* n, int s) { // описание функции вывода
cout<
#include
#include
#include
#define FAM 25
#define DOL 15
// объявление структуры -."Сотрудники "
struct SOTR{
char fam [FAM]; // фамилия
char dol[DOL]; // должность
// отдел
int otdel;
};
void sozdanie();
//прототип функции : " Создание "
void prosmotr();
// прототип функции : " Просмотр "
// операция-функция ввода в структуру с клавиатуры
istream &operator >> (istream &in, SOTR &x) {
cout<<"\nФамилия:";
in.seekg(0,ios::end);
in.get(x.fam,FAM-1,'\n');
cout<<"\nДолжность:";
in.seekg(0,ios::end);
in.get(x.dol,DOL-1,'\n');
cout<<"\nОтдел:";
in.seekg(0,ios::end);
in >> x.otdel;
return in;
}
/ / операция-функция вывода структуры на дисплей
ostream &operator << (ostream &out, SOTR x){ / / печать объекта
out << "\n|" << x.fam << "|" << x.dol << "|" << x.otdel <<"|";
return out;
}
/ / операция-функция ввода структуры c МД
ifstream &operator >> (ifstream &in, SOTR
&x){ in.setf(ios::left); in.width(FAM);
in.get(x.fam,FAM,'\n'); in.width(DOL);
in.get(x.dol,DOL,'\n'); in >> x.otdel;
return in;
}
/ / операция-функция вывода структуры на МД
ofstream &operator << (ofstream &out, SOTR &x) {
out.width(FAM-1); out.setf(ios::left); out << x.fam;
out.width(DOL-1);
out.setf(ios::left); out << x.dol; out << x.otdel;
return out;
124
}
void main(void) {
clrscr();
char c;
while (1) {
cout << endl << "1. Создание файла";
cout << endl << "2. Просмотр
содержимого"; cout << endl << "3. Выход";
cout << endl << "Ваш выбор -> ";
cin.seekg(0,ios::end);
c = cin.get();
switch(c){
case '1':
sozdanie();
break;
case '2':
prosmotr();
break;
case '3':
return;
default:
cout << "Вводите только цифры от 1 до 3" << endl;
}
}
}
void sozdanie(){
char c;
// поток ff для вывода файла
kniga.txt ofstream ff;
// создание экземпляра объекта
SOTR s;
ff.open("kadry.txt", ios::binary );
// цикл записи элементов в файл
do{
cin >> s;
// ввод с клавиатуры
ff << s;
// вывод в файл
cout<<"\nПродолжить ввод?(Y/N\ или Д/Н)";
}
while ((c = getch())=='y'||c=='Y'||c=='д'||c=='Д');
ff.close(); // закрытие файла
}
void prosmotr(){
ifstream finp;
125
SOTR s;
// поток finp для ввода из файла kniga.txt
finp.open("kadry.txt", ios::binary);
finp.seekg(0,ios::beg); cout<<"\nСписок
элементов из файла\n"; while ( finp ) { //
пока не конец файла
finp >> s ; // вывод из файла
cout << s; // вывод на дисплей
}
finp.close(); // закрытие файла
} ________________________________________________________________
Результаты работы программы:
1. Создание файла
2. Просмотр содержимого
3. Выход
Ваш выбор -> 1
Фамилия: Иванов
Должность: лаборант
Отдел: 12
Продолжить ввод?(Y/N или Д/Н)
Y
Фамилия: Петров
Должность: инженер
Отдел: 15
продолжить ввод?( Y/N или Д/Н)
N
1. Создание файла
2. Просмотр содержимого
3. Выход
Ваш выбор -> 2
Фамилия
Иванов
Петров
Должность
лаборант
инженер
Отдел
12
15
Рис. 15.4
126
16. Классы
16.1. Определение класса
Программисты на С основное внимание уделяют написанию функций, тогда
как при программировании на С++ большее внимание уделяется созданию классов. Классы - это типы данных, определяемые программистом. Каждый класс содержит данные и набор функций, манипулирующих с этими данными. Компоненты - данные класса называются данными-элементами. Компоненты - функции
класса называются функциями-элементами. Класс, определяемый пользователем,
называется объектом. Классы в С++ являются естественным продолжением структуры (struct) в С. Прежде чем рассматривать специфику разработки классов, проанализируем недостатки структуры от которых удается легко избавиться в классах.
Программа на рис. 16.1 создает определенный пользователем тип структуры
Time с тремя целыми элементами: hour, minute, second.
//Создание структуры, задание и печать ее элементов
#include
#include
struct Time {
// определение структуры
int hour;
// 0-23
int minute;
// 0-59
int second;
// 0-59
};
void printMilitary (int, int, int);
// прототип
void printStandard (int, int, int);
// прототип
main (){
Time dinnerTime;
// переменная нового типа Time
dinnerTime.hour = 18; // задание элементам правильных значений
dinnerTime.minute = 30;
dinnerTime.second = 0;
cout << "Обед состоится в ";
printMilitary(dinnerTime.hour, dinnerTime.minute, dinnerTime.second );
cout << "по военному времени," << endl << "что соответствует"; cout
<< "по стандартному времени," << endl;
127
printStandard(dinnerTime.hour, dinnerTime.minute, dinnerTime.second);
dinnerTime.hour = 29;
// задание элементам неправильных значений
dinnerTime.minute = 73;
dinnerTime.second = 103;
cout << endl << "Время с неправильными значениями:";
printMilitary(dinnerTime.hour, dinnerTime.minute, dinnerTime.second);
cout << endl;
getch();
return 0;
}
// Печать времени в военном формате
void printMilitary (int h, int m, int s){
cout << ( h < 10 ? "0" : "") << h
<< ":" << (m < 10 ? "0" : "") << m
<< ":" << (m < 10 ? "0" : "") << s ;
}
// Печать времени в стандартном формате
void printStandard(int h, int m, int s){
cout << ( h == 0 || h == 12 ? 12 : h %12 )
<< ":" << (m < 10 ? "0" : "") << m
<< ":" << (m < 10 ? "0" : "") << s ;
} ____________________________________________________________
Результаты работы программы:
Обед состоится в 18: 30: 00 по военному времени, что соответствует 6: 30: 00 РМ по стандартному времени.
Время с неправильными значениями: 29: 73: 103
Рис. 16.1.
Программа определяет единственную структуру типа Time, названную dinnerTime, и использует операцию точка для присвоения элементам структуры
начальных значений 18 для hour, 30 для minute и 0 для second. Затем программа
печатает время в военном (24-часовом) и стандартном (12-часовом) форматах.
Основной недостаток структур связан с тем, что в них не существует никакого
интерфейса по оценке правильности использования типов данных и оценки противоречивости их начальных значений. Существуют и другие проблемы, связанные со структурами в стиле С. В С структуры не могут быть напечатаны как еди-
128
ное целое, а их печать возможна только по одному элементу с соответствующим
форматированием каждого.
Классы предоставляют программисту возможность моделировать объекты,
которые имеют атрибуты, представленные как данные - элементы и варианты поведения или операции, представленные как функции - элементы. Типы, содержащие данные - элементы и функции - элементы, определяются в С++ с помощью
ключевого слова classs. Когда класс определен, имя класса может быть использовано для объявления объекта этого класса.
На рис. 16.2 дано простое определение класса Time. Определение класса
начинается с ключевого слова classs. В фигурных скобках записывается тело класса, а его определение заканчивается точкой с запятой. Определение класса Time,
как и в структуре, содержит три целых элемента:
hour, minute и second.
//Простое определение класса Time class Time {
public:
Time();
void setTime (int, int, int); void printMilitary();
void printStandart();
private:
int hour; // 0-23 int minute; // 0-59 int second; //
0-59
};
Рис. 16.2
Остальные части определения класса новые. Метки public: (открытая) и private:
(закрытая) называются спецификаторами доступа к элементам. Любые данные и
функции-элементы, объявленные после спецификатора доступа public:, доступны
при любом обращении программы к объекту класса Time, а эти же эле-менты,
объявленные после спецификатора доступа private:, доступны только функциямэлементам
этого
класса.
Спецификаторы
доступа
к
элементам
всегда
заканчиваются двоеточием и могут появляться в определении класса много раз и
129
в любом порядке. Определение класса (рис.16.2) содержит после спецификатора
доступа public: прототипы следующих четырёх функций элементов: Time, setTime,
printMilitary, printStandart. Это - открытые функции-элементы или открытый ин-
терфейс услуг класса. Эти функции будут использоваться для манипуляций с
данными класса. Функция-элемент с тем же именем, что и класс, называется конструктором этого класса. Конструктор – это специальная функция-элемент, которая присваивает начальные значения данным-элементам этого класса. После спецификатора private: следуют три целых элемента. Они являются доступными
только функциям-элементам класса, т. е. функциям, прототипы которых включены в определение этого класса. Когда класс определен, его можно использовать в
качестве типа в объявлениях, например, следующим образом:
Time sunset.
ArrayOfTimes[5] .
*pointerToTime.
&dinnerTime = sunset;
// объект типа Time
// массив объектов типа Time
// указатель на объект типа Time
// ссылка на объект типа Time
Программа на рис.16.3 использует класс Time и создает единственный объект класса Time, названный t. После создания объекта, автоматически вызывается
конструктор Time, который явно присваивает нулевые начальные значения всем
данным-элементам закрытой части private. Затем печатается время в военном и
стандартном форматах, с тем, чтобы подтвердить, что элементы получили правильные начальные значения. После этого с помощью функции-элемента setTime
устанавливается время, и оно снова печатается в обоих форматах. Затем функцияэлемент setTime пытается дать данным-элементам неправильные значения, и время снова печатается в обоих форматах.
// Определение класса Time
class Time {
public :
Time ( );
void setTime(int, int, int );
void printMilitary( );
void printStandard( );
// конструктор
// установка часов, минут и секунд
// печать времени в военном и
// стандартном форматах
130
private:
int hour ;
int minute;
int second;
};
// 0 - 23
// 0 - 59
// 0 - 59
/*Конструктор Time ( ) ; присваивает нулевые начальные значения
каждому элементу данных и обеспечивает согласованное начальное
состояние всех объектов Time ( ) ; */
Time :: Time( ) { hour = minute = second = 0; }
/ * Задание нового значения Time в военном формате.
Проверка правильности значений данных.
Обнуление неверных значений. */
void Time : : setTime( int h, int m, int s ){
hour = ( h>= 0 && h < 24 ) ? h : 0; minute =
(m>=0 && m<60) ? m : 0; second = (s >= 0
&& s < 60) ? s : 0;
}
// Печать времени в военном формате
void Time : : printMilitary( ){
cout << ( hour < 10 ? "0" : "") << hour
<< ":" << (minute < 10 ? "0" : "") << minute
<< ":" << (second < 10 ? "0" : "") << second ;
}
// Печать времени в стандартном формате
void Time : : printStandard( ){
cout << ( (hour == 0 || hour == 12 ) ? 12 : hour %12 )
<< ":" << (minute < 10 ? "0" : "") <
#include
133
class Count { // простой класс Count
public:
int x;
void print () { cout << x << endl; }
};
main ( ){
Count counter,
// создает объект counter
* counterPtr = &counter ,
// указатель на counter
& counterRef = counter;
// ссылка на counter
cout << " Присвоение x значения 7 и печать по имени объекта :";
counter.x = 7;
// присвоение 7 элементу данных x
counter.print ( ) ;
// вызов функции-элемента для печати
cout << " Присвоение x значения 8 и печать по ссылке :";
counterRef.x = 8;
// присвоение 8 элементу данных x
counter.print ( ) ;
// вызов функции-элемента для печати
cout << " Присвоение x значения 10 и печать по указателю :";
counterPtr ->x = 10;
// присвоение 10 элементу данных x
counterPtr->print ( ) ;
// вызов функции-элемента для печати
} ___________________________________________________________________
Результаты работы программы:
Присвоение x значения 7 и печать по имени объекта : 7
Присвоение x значения 8 и печать по ссылке : 8
Присвоение x значения 10 и печать по указателю : 10
Рис . 16.4
На рис 16.5 показан усложненный вариант этой программы на примере
класса Clients (Клиенты). В этом классе используются открытые элементы данных
account (счет) типа int и sum(сумма) типа float, а также открытая функция-элемент
print( ). Программа создает три экземпляра переменных типа Clients -vcladchik
(вкладчик), vcladchikRef (ссылка на объект типа Clients) и vcladchik Ptr (указатель
на объект типа Clients). Переменная vcladchik Ref объявлена, чтобы ссылаться на
vcladchik, а переменная vcladchik Ptr объявлена, чтобы указывать на vcladchik.
// Демонстрация операций доступа к элементам класса . и ->
#include
#include
#include
#include
class Clients{
public:
// простой класс Clients
134
int account;
char name[20];
float sum;
void print () { cout<account = 3; // присвоение 3 элементу данных account
strcpy(vcladchikPtr->name,"Mary");
vcladchikPtr ->sum = 858.35; // присвоение значения элементу данных sum
vcladchikPtr ->print ( ) ; // вызов функции - элемента для печати getch();
return 0;
} _________________________________________________________________________
Результаты работы программы:
Присвоение счет =1,имя= Bobby, сумма=125.45 и печать по имени объекта:
счет= 1
имя = Bobby
сумма = 125.45
Присвоение счет =2,имя=Каt, сумма=458.95 и печать по ссылке: счет= 2
имя = Bobby
сумма = 125.45
Присвоение счет =3,имя=Магу, сумма=858.35 и печать по указателю: счет= 1
имя = Boby
сумма = 125.45
Рис. 16.5.
135
На рис. 16.6 показан вариант программы, в которой с помощью функцииэлемента print() осуществляется форматированный вывод данных на печать. Как
и ранее, функция print() не получает никаких аргументов, потому что она печатает данные-элементы определенного объекта типа Clients. Это уменьшает вероятность появления ошибки при передаче аргументов. В этом классе используются
открытые элементы данных account (счет) типа int и sum (сумма) типа float, а
также открытая функция - элемент print( ). Программа создает три экземпляра
переменных типа Clients - vcladchik(вкладчик), vcladchikRef (ссылка на объект
типа Clients) и vcladchikPtr (указатель на объект типа Clients). Переменная
vcladchikRef объявлена, чтобы ссылаться на vcladchik, а переменная vcladchikPtr
объявлена, чтобы указывать на vcladchik.
// Демонстрация операций доступа к элементам класса . и ->
#include
#include
#include
#include
#include
class Clients { // простой класс Clients
public:
int account;
char name[20];
float sum; void
print ();
};
//описание функции-элемента рrint(), принадлежащей классу Clients
void Clients::print () {
cout<<"\nсчет "<account = 3; // присвоение 3 элементу данных account
strcpy(vcladchikPtr->name,"Mary");
vcladchikPtr ->sum = 858.35; //присвоение значения элементу данных sum
vcladchikPtr ->print ( ) ; // вызов функции - элемента для печати
getch();
return 0;
}
void print(int a, char* n, float s)
{
cout<<" счет "<
#include
#include
#include
#include
class Clients
// простой класс Clients
{
public:
int account;
char name[20]; float
sum;
void print ();
};
// описание функции-элемента print ();принадлежащей классу Clients
void Clients::print (){
cout<>vcladchik[i].account>>vcladchik[i].name>>vcladchik[i].sum;
cout<<"?"<= 0 && h < 24 ) ? h : 0;
minute = (m>=0 && m<60) ? m : 0;
second = (s >= 0 && s < 60) ? s : 0;
}
void Time : : printMilitary( ) // печать времени в военном формате
{
cout << ( hour < 10 ? "0" : "") << hour
<< ":" << (minute < 10 ? "0" : "") << minute << ":"
<< (second < 10 ? "0" : "") << second ;
}
140
void Time : : printStandard( ) // печать времени в стандартном формате
{
cout << ( (hour == 0 || hour == 12 ) ? 12 : hour %12 )
<< ":" << (minute < 10 ? "0" : "") <
/* Функция-конструктор для задания начальных значений
закрытых данных, вызывает функцию-элемент setTime, чтобы установить
значения переменных, которые по умолчанию равны нулю.*/
Time :: Time( int hr, int min, int sec) { setTime( hr, min, sec) ; }
// Установка значений часов, минут, секунд
void Time::setTime(int h, int m, int s)
{
hour = (h >= && h<24) ? h : 0 ;
minute = (m>= 0 && m< 60) ? m : 0;
second = (s >=0 && s < 60) ? s : 0;
};
// Установка значения часа
void Time::setHour(int h) { hour = (h>= 0 && h < 24) ? h : 0;}
// Установка значения минут
void Time:: setMinute(int m) { minute = (m>=0 && m<60) ? m : 0;}
// Установка значения секунд
void Time:: setSecond(int s) { second = (s >= 0 && s < 60) ? s : 0;}
//Получение значения часа
int Time :: getHour() {return hour;}
int Time :: getMinute () {return minute;}
int Time :: getSecond () {return second;}
// Печать времени в военном формате
void Time :: printMilitary( )
{
cout << ( hour < 10 ? "0" : "") << hour
<< ":" << (minute < 10 ? "0" : "") << minute
<< ":" << (second < 10 ? "0" : "") << second ;
}
// Печать времени в стандартном формате
void Time :: printStandard( )
{
cout << ( (hour == 0 || hour == 12 ) ? 12 : hour %12 )
<< ":" << (minute < 10 ? "0" : "") <
// #include "time3. h "
void incrementMinutes( Time &, int);
main ()
{
Time t; t.setHour(17);
t.setMinute(34);
t.setSecond(25);
cout << "Результат установки всех правильных значений:"<<
endl <<" Час: " << t.getHour()
<<" Минуты: " << t.getMinute ()
<<" Секунды: " << t.getSecond () << endl <
class Count {
friend void setX(Count &, int);
//объявление друга
public:
Count() { x = 0; }
// конструктор
void print() const {cout << x << endl; }
// вывод
private :
int x;
// элемент данных
};
void setX(count &c, int val)
{
c.x = val;
}
main ()
{
count object;
cout << "object.x после своего создания :";
object.print();
147
cout << "object.x после вызова дружественной функции setX:";
setX(object, 8);
object.print();
return 0;
} ___________________________________________________________________
object.x после своего создания: 0
object.x после вызова дружественной функции setX: 8
Рис. 16.10
Другой пример использования дружественной функции для доступа к закрытому элементу приведен на рис.16.11. В программе используется дружественная функция SUM() для установки закрытого элемента данных sum (сумма вклада) класса Clients.
Конструктор задает закрытому элементу данных sum начальное нулевое
значение. Передача параметров в дружественную функцию осуществляется по
ссылке.
// Пример использования дружественной функции
#include
#include
#include
#include
#include
// Простой класс Clients
class Clients {
friend void SUM (Clients& , float ) ; //объявление дружественной функции
public:
Clients() { sum=0; }
// конструктор
// Открытые данные - элементы
int account;
// номер счета
char name[20];
// массив символьных переменных
void print ();
// функция-элемент
private:
float sum;
// закрытые данные-элементы
};
void SUM(Clients& c, float val)
// описание дружественной функции
{
c.sum = val;
};
void Clients::print ()
// описание функции-элемента
148
{
cout<