
- •Часть 1
- •18 Сентября 2012 г., протокол № 1
- •Предисловие
- •§ 1. Основные понятия программы
- •1.2. Директива препроцессора #include
- •Структура программы
- •Комментарии
- •Ключевые слова, идентификаторы, переменные
- •§ 2. Ввод, вывод
- •§ 3. Выражения
- •3.1. Константы
- •Непосредственно записать в выражении;
- •3.2. Операции и их приоритет
- •3.3. Операции отношения и логические операции
- •3.4. Операция присваивания
- •§ 4. Программирование ветвлений
- •4.1. Оператор if
- •4.2. Тернарная операция
- •4.3. Оператор выбора switch
- •Г л а в а 2 циклы
- •§ 1. Оператор while
- •§ 2. Оператор break
- •§ 3. Оператор do … while
- •§ 4. Оператор for
- •Г л а в а 3 Введение в массивы
- •§ 1. Основные понятия
- •§ 2. Способы определения массивов
- •§ 3. Вывод массива. Функции printf и cprintf
- •§ 4. Типы задач при работе с массивами
- •Упражнения и тесты
- •Г л а в а 4 модульное программирование. Функции
- •§1. Функции без результатов. Параметры-значения
- •1.1. Примеры. Правила оформления и вызова функций
- •Формальные и фактические параметры
- •Передача параметров по значению
- •§ 2. Функции типа void с результатами
- •2.1. Вычислeниe бeсконeчных сумм
- •2.2. Что такое ссылочный тип
- •2.3. Параметры ссылочного типа
- •§ 3. Функции, отличные от void. Оператор return
- •§ 4. Область действия имён. Локальные и глобальные имена
- •§ 5. Встраиваемые (inline) функции
- •§ 6. Параметры по умолчанию
- •§ 7. Перегрузка функций
- •7. Сравнение функций типа void и отличных от типа void.
- •Г л а в а 5 введение в объектно-оРиентированное программирование
- •§ 1. Первое знакомство с ооп
- •§ 2. Класс. Поля и методы класса
- •§ 3. Создание объектов. Конструктор
- •Упражнения и тесты
- •Оглавление
- •3.1. Константы …………………………………………….………..….........…8
- •Упражнения и тесты …….………………..………………………….…………....19
- •Методы программирования:
- •Лекции, примеры, тесты
- •Пособие для студентов механико-математического факультета
- •В двух частях
- •Часть 1
§ 6. Параметры по умолчанию
В прототипе или при описании функции в её заголовке одному или нескольким формальным параметрам может быть назначено значение по умолчанию по тем же правилам, что при инициализации. При вызове таких функций фактические параметры, соответствующие умалчиваемым, могут быть опущены, и тогда функция будет выполняться с теми значениями, которые указаны в заголовке. Значение по умолчанию можно изменить, записав при вызове фактический параметр вместо умалчиваемого. Остальные фактические параметры без умалчиваемых значений надо обязательно задать.
Пример 6. Дана функция
void fun6 (float f, char ch='*', int i=2)
{ cout<<endl<<f<<" "<<ch<<" "<< i; }
Приведём несколько вариантов её вызова.
main() { fun6 (2.5, ‘-‘, 11); /* f=2.5, ch=’-‘, i=11 */
fun6 (2.5, 196); /* f=2.5, ch — символ с кодом 196 (‘-‘), i=2 по умолчанию. */
fun6 (196); /* f=196 как вещественное, сh=’*’ и i=2 по умолчанию. */
fun6 (0.01, ‘*’, 33); /* f=0.01, ch=’*’, i=33; */ getch(); }
Правила передачи параметров по умолчанию:
значение по умолчанию может быть задано либо в прототипе, либо при описании функции, но только в одном месте один раз;
в качестве умалчиваемых значений должны быть либо константы, либо глобальные переменные;
умалчиваемые параметры должны быть последними в списке;
если при вызове функции опустили аргумент для параметра по умолчанию, то не надо писать аргументы и для всех оставшихся в списке. Например, если для функции fun6 надо изменить i на 196, то fun6 (0.11, 196); компилируется из-за совместимости целого и символьного типов, но неправильно выполняется, так как i останется по умолчанию, а fun6(0.11 , , 196); не компилируется. Правильным будет такой вызов: fun6(0.11 , ’*’ , 196);, в котором второй параметр (’*’) записываем, несмотря на то, что это умалчиваемое значение.
Рекомендуется упорядочить умалчиваемые параметры по частоте их изменения.
Пример 7. Пусть вещественный параметр f1 меняется почти при каждом вызове. Из остальных трёх параметров вещественный параметр f2 меняется чаще, чем символ, а вероятность изменения целого параметра наименьшая. Тогда задаём следующий порядок параметров:
int test=2;
void Out7 ( float f1, float f2=1.1,char c3='*', int p4=test )
{ textcolor(11); cprintf("%f %f %c %d\n\r", f1, f2, c3, p4); }
int main() { Out7(11); Out7(11,2.2); Out7(11,2.2, 65);
int q1=11; Out7(q1, 'A' ,196 ,9);
getch(); return 0; }
В качестве умалчиваемого значения для целого p4 задали значение глобальной переменной, а не константы, как для других параметров. Целое число q1 и символ 'A' можно передать вместо вещественных f1 и f2. Будет выведено 11.000000 и код символа 'A' , то есть целое число, преобразованное в вещественное: 65.000000.
Упражнение. Определить остальные результаты вызова функции Out7.
§ 7. Перегрузка функций
В “старом” языке С все имена функций должны быть уникальны в одном проекте. Это плохо и неудобно при работе с функциями, которые выполняют одинаковые или похожие действия с разными типами данных. Классический пример этого — стандартные функции с разными именами abs(x) и fabs(x), которые возвращают абсолютное значение, соответственно, целого и вещественного типа. В С++ можно определить несколько функций с одним и тем же именем, которые отличаются типами параметров и реже их количеством. Тогда говорят, что функции перегружены.
Пример 8. Опишем и будем использовать три функции, которые переставляют значения двух переменных разных типов:
void RR ( int &, int &);
void RR ( float &, float &);
void RR ( char &, char &);
/* Напомним, что в прототипе имена переменных можно не писать, а обязательны только их типы */
int main()
{ int i1=11, i2=22; RR(i1,i2);
cout<<"\ni1="<<i1<<" i2="<<i2<<endl;
float f1=3.4, f2=5.6; RR(f1,f2);
cout<<"\nf1="<<f1<<" f2="<<f2<<endl;
char c1='a', c2='b'; RR(c1,c2);
cout<<"\nc1="<<c1<<" c2="<<c2<<endl;
getch(); return 0; }
void RR(int &u, int &v) { int t=u; u=v; v=t; }
void RR(float &u, float &v)
{ float t=u; u=v; v=t; }
void RR(char &c1, char &c2)
{ char c=c1; c1=c2; c2=c; }
Какой вариант функции RR из трёх вызывается в main()? Компилятор автоматически выберет необходимую версию функции на основании типа используемых в функции фактических параметров. Первый раз вызывается первый вариант функции для целых параметров, второй раз — для вещественных значений и, наконец, для символьных.
Разрешается перегружать функции, отличающиеся количеством параметров. Тогда конкретный вариант функции компилятор выбирает на основании количества используемых в функции фактических параметров. Например, можно перегрузить функцию для вывода даты в виде строки или в виде трёх целых чисел.
Нельзя, чтобы перегружаемые функции отличались только типом возвращаемых значений. Например, такая перегрузка функций
int FUN2(int ); float FUN2 (int); компилятору “не понравится”!
Упражнения и тесты
Изменить функцию SINCOS (§ 2) таким образом, чтобы оба результата, и y и z, вычислялись в одном цикле одновременно.
В функции SINCOS (§ 2) заменить оператор do … while на while.
В функции SINCOS (§ 2) заменить оператор do … while на for.
В функции main (см. 2.1) вместо оператора for записать while.
Вместо одной функции SINCOS типа void cоставить и использовать две отличные от void функции для вычисления y и z.
Сравнение параметров ссылочного типа и параметров - значений.
Что будет выведено?
void fun1(int a, int &b, int &c) { int d; a=…; b=…; c=…; d=…;
/* Вместо многоточия числа или выражения */
cout<<a%c<<" "<<(b / d )<<endl; }
int main()
{ int u=…, v=…, w=…, z=…;
/* Вместо многоточия числа или выражения */ fun1(u,v,w);
cout<<u<<" "<<v<<" "<<(w + z)<<endl;
getch(); return 0; }
Заголовок функции может быть другим, зависит от того, какие параметры объявлены со ссылочным типом:
void fun1(int a, int b, int &c)
// или void fun1(int &a, int &b, int c) и т.п.