Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Учебное пособие_С++2015

.pdf
Скачиваний:
179
Добавлен:
15.03.2016
Размер:
2.8 Mб
Скачать

xij

x ji

 

yij y ji

 

2

, x- матрица , симметричная матрице y.

 

 

 

Решение:

#include "stdafx.h"

void SM(float Y[4][4], int n, float X[4][4])/*безтиповая

функция вычисления симметричной матрицы X из исходной матрицы Y */

{

int i,j;

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

{

X[i][j]=(Y[i][j] + Y[j][i])/2; X[j][i] =X[i][j];

}

}

void VIVOD(float Y[4][4], int n)//функция вывода матрицы

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

{for (j=0;j<n;j++) printf("%8.2f",Y[i][j]); printf("\n");

}

}

void main()

{

float A[4][4], B[4][4], C[4][4], D[4][4]; int i, j;

printf("Введите матрицу А\n"); for (i=0;i<3;i++)

for (j=0;j<3;j++) scanf("%f",&A[i][j]); printf("Введите матрицу B\n");

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

for (j=0;j<4;j++) scanf("%f",&B[i][j]);

SM(A, 3,C); // обращение к функции SM для матрицы A SM(B, 4, D); //обращение к функции SM для матрицы B

printf("Симметричная матрица С\n"); VIVOD(C,3);

printf("Симметричная матрица D\n"); VIVOD(D,4);

}

111

Перегрузка функций в С++

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

float SUM (float a, float b);

//принимает два вещественных аргумента и возвращает результат ввиде вещественного числа

double SUM (double a, double b);

//принимает два аргумента вещественного длинного типа и возвращает результат ввиде вещественного длинного числа

complex SUM (complex a, complex b);

//принимает два аргумента комплексного типа и возвращает результат ввиде комплексного числа

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

Наполним функцию SUM банальным алгоритмом вычисления суммы двух чисел.

#include "stdafx.h"

#define complex struct compl complex

{double x,y;};

complex SUM (complex a, complex b) {complex c;

c.x=a.x+b.x;

c.y=a.y+b.y; return c;

}

float SUM (float a, float b) {float c;

c=a+b;

112

return c;

}

double SUM (double a, double b) { double c;

c=a+b; return c;

}

int _tmain(int argc, _TCHAR* argv[]) {complex z,m,n;

float a=4.5,b=6.5; z.x=5; z.y=6; m.x=7;m.y=8;

n= SUM (z,m); //вызов функции SUM для комплексных параметров printf("%lf+i%lf\n",n.x,n.y);

float d= SUM (a,b); //вызов функции SUM для вещественных параметров printf("d=%f\n",d);

float d2= SUM (10,35); //неоднозначный вызов функции SUM printf("d2=%f\n",d2);

return 0;

}

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

По типам фактических параметров компилятор определяет, какую именно функцию требуется вызвать. Этот процесс называется разрешением перегрузки (перевод английского слова resolution в смысле «уточнение»). Тип возвращаемого функцией значения в разрешении не участвует. Механизм разрешения основан на достаточно сложном наборе правил, смысл которых сводится к тому, чтобы использовать функцию с наиболее подходящими аргументами или выдать сообщение, если такой не найдется. Если точного соответствия не найдено, выполняется преобразование типов параметров по общим правилам, например char в int; int во float, float в double;

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

113

могут быть преобразованы как в тип float, так и в тип double, что и вызовет возникновение ошибочной неоднозначности.

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

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

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

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

Функции не могут быть перегружены, если описание их параметров

отличается только модификатором const (например, int и const int ),

или использованием ссылки (например, int и int&).

Более подробную информацию о перегруженных функциях см. в [2].

Рекурсия

В теле функции известны все объекты, описанные во внешнем блоке, т.е. все глобальные переменные и имя самой функции.

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

Рекурсия возможна благодаря тому, что при вызове функции создаются новые экземпляры локальных переменных, которые сохраняются во внутреннем стеке машины. Стек функционирует по принципу LIFO - Last In – First Out (последний вошел – первый вышел).

Переменные помещаются в стек одна за другой и выбираются из стека в обратном порядке.

Обязательным элементом всякого рекурсивного процесса является

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

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

Рассмотрим пример вычисления факториала 5.

5! 1 2 3 4 5 , где

1 2 3 4 - это 4!

т.е

5!=4! 5

 

Факториал нуля равен 1. Отсюда формула вычисления N-факториала:

114

1, если N 0

N!

(N 1)! N, если N 0

Реализуем вычисление факториала в виде функции:

#include "stdafx.h"

float fact(int N) //рекурсивная функция вычисления факториала числа N

{

if (N==0)

Обращение функции к самой себе

 

return 1;

 

else

 

return (fact(N-1)*N);

}

void main()

{int N=15;

printf("факториал 15=%f\n",fact(N)); /* обращение к

функции fact для вычисления факториала 15 */

}

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

Пример: Вычислить количество нулей в массиве А[10].

# include "stdafx.h" const int n=10;

int kol(int i,int *A) { if (i==n) return 0;

else {

if(A[i]==0) return kol(i+1,A)+1; else return kol(i+1,A);

}

}

int main()

{int y[]={1,0,2,5,4,0,1,3,0,4,3}; int x=kol(0,y);

printf("количество нулей=%d\n",x);

}

Технология сборки библиотеки

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

115

Заголовочный файл (иногда головной файл, англ. header file), или подключаемый файл, в языках программирования С и C++ - это файл с расширением .h. Заголовочный файл в общем случае может содержать любые конструкции языка программирования, но на практике в него помещают объявления идентификаторов, которые должны быть объявлены более чем в одном файле вашей программы, объявления структур, прототипы функций, перечисления, макросы препроцессора. Основная цель использования заголовочных файлов — вынесение описания нестандартных типов и функций за пределы основного файла с кодом. Заголовочный файл используется путѐм включения его текста в использующий его файл директивой препроцессора #include. Чтобы избежать повторного включения одного и того же кода,

используются директивы #ifndef, #define, #endif.

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

Пусть создаваемая нами библиотека состоит из заголовочного файла mylib.h и файла mylib.cpp.

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

Перечислим все эти функции:

maximum(). Находит и возвращает наибольшее из двух чисел.

Form_matrix(). Заполняет матрицу, адрес которой передается ей в качестве параметра, случайными числами.

ST(). Возводит любое число в степень n.

Z(). Меняет значение переданного ей параметра на случайное число.

Sum(). Вычисляет среднее арифметическое вектора, адрес и размер которого передаются в качестве параметров.

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

SM(). Безтиповая функция вычисляет симметричную матрицу из исходной матрицы.

PK(). Безтиповая функция переводит декартовые координаты точки в полярные.

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

116

#define MYLIB_H
const nmax = 50; // максимальная размерность матрицы
int maximum(int a, int b);//прототип функции maximum void Form_matrix(int A[][M], int m, int n); /*прототип функции Form_matrix */
// если MYLIB_H еще не определили, то определяем
#ifndef MYLIB_H
Текст файла

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

fact(). Рекурсивная функция вычисляет факториал числа n.

mylib.h:

float ST(float x, int n); //прототип функции ST

void

Z

(int

у); //прототип функции Z

void

Z

(int

*у); //прототип функции Z

int Sum ( int A[], int N ); //прототип функции Sum

void product(int А[][nmax], int В[][nmax],int С[][nmax], int m, int n, int k); //прототип функции product

void SM(float Y[4][4], int n, float X[4][4]); /*прототип функции SM */

void PK(float a, float b, float *ro, float *fi); /*прототип функции PK */

int CP(float D[7][7], int m, int n); //прототип функции CP float Mod_Otk(float *a, int n); //прототип функции Mod_Otk float fact(int N); //прототип функции fact

#endif /* MYLIB_H */

Файл mylib.cpp является созданной нами библиотекой, в которой содержатся реализации всех перечисленных выше функций.

В целях экономии текст файла mylib.cpp приведем не полностью.

Текст файла mylib.cpp:

#include "stdafx.h" #include "stdlib.h"

{

int Sum ( int A[], int N )//заголовок функции Sum

{

117

int i, sum; //локальные переменные sum = 0;

for ( i = 0; i < N; i ++ ) sum += A[i];

return sum/N; //возвращаемое значение

}

void product(int А[][nmax], int В[][nmax],int С[][nmax], int m, int n, int k) //заголовок функции product

{

/* m - число строк в матрице А;

n - число строк в матрице В и число столбцов в матрице А; k - число столбцов в матрице В. */

for (int i=0; i< m; i++) for (int j=0; j< k; j++)

{

С[i][j]=0;

for (int l=0; l< n; l++) С[i][j] + = А[i][l]*В[l][j];

}

}

float fact(int N) //рекурсивная функция вычисления факториала числа N

{

if (N==0) return 1;

else

return (fact(N-1)*N);

}

Текст файла, использующего созданную библиотеку

#include "mylib.h"//подключение заголовочного файла библиотеки mylib

// главная функция выполняет основную работу программы int main()

{

int a[nMax][nMax],b[nMax][nMax]; //объявление матриц

// здесь могут быть обращения к функциям библиотеки return 0;

}

118

2.12. Текстовые данные

Вязыке С++ текстовая информация представляется двумя типами данных:

спомощью символов и строк.

Символьный тип данных

Значением данных символьного типа является любой символ из набора всех символов компьютера или его код. Каждому символу соответствует порядковый номер (код) в диапазоне 0..255. Для кодировки символов первой половины диапазона используется код ASCII (американский

стандартный

код

для

обмена

информацией),

или более современные стандарты в последних версиях языка Си.

При написании программ символьные данные

могут быть

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

 

 

Символьная

константа

представляет

собой

одиночный

символ, заключенный в апострофы, например:

 

 

 

’Y’

’!’

’_’

’Д’

 

Символьная переменная объявляется с помощью ключевого слова char, например:

char cr;

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

Ввод-вывод символьных данных

Для ввода символьных данных используются функции: scanf() – форматированный ввод, getchar() или getch() – специальные функции для ввода символа. Для форматного ввода и вывода символьных констант используется спецификатор (формат) %с. Необходимо помнить, что нажатие любой небуквенной клавиши при вводе ([пробел], [Enter] и др.) будет значимым и восприниматься как символ.

Пример 1. Организовать ввод символьных переменных:

a='i' b='j' c='k'

main()

{char a,b,c;

printf("Введите исходные данные"); scanf("%c%c%c",&a,&b,&c);

. . .

}

119

При вводе символы набираются без апострофов и пробелов: ijk [Enter]

Символ клавиши [Enter] выходит за пределы списка ввода, поэтому он игнорируется.

При вводе символьной информации с помощью функции getchar() надо помнить, что функция переводит программу в состояние ожидания, но при нажатии клавиши символ не выводится на экран. А, например, при выполнении следующего фрагмента программы

printf("Введите исходные данные");

a=getch();b=getch();c=getch();

значения переменных надо вводить как и в предыдущем примере, но на экране их значения не отразятся.

Для вывода символьных данных используются функции printf() и putchar().

Пример 2. Организовать вывод указанных выше переменных на экран в одну строку. Запись оператора вывода будет следующей:

printf("%c%c%c\n",a,b,c);

Нa экране будет отображено:

ijk

Если использовать для вывода функцию putchar():

putchar(a); putchar(b); putchar(c);

на экране будет отображен тот же результат.

Обработка символьных данных

Поскольку символы в языке С++ упорядочены, к ним можно применять операции отношения (>, >=, <, <=, ==, !=). Это дает возможность записывать логические выражения с символьными данными в условных операторах. Например, оператор

if (ch =='!' ) ch ='.';

сравнивает значение переменной ch с символом ‗!‘ и в случае их равенства следующая команда заменяет в символьной переменной ch восклицательный знак точкой.

120