Урок 3. C++ Ссылочные переменные

На занятии объясняется тема «язык С++: аргументы по ссылке»

C++ аргументы по ссылке

Еще о цикле for

В цикле for можно использовать несколько счетчиков: область инициализации счетчика, так же как и область изменения счетчика может состоять из нескольких операторов, разделенных запятой «,»

int a = 0, b = 0;
for (a = 1, b = 8; a < b; ++a, --b) {
    // empty body
}
cout << a << " " << b << endl; // вывод: 5   4

Ссылки

Ссылка — это не объект, это другое имя объекта; ссылки не занимают место в памяти:
Пример 1:

int i = 5;
int &pi = i;   // ссылка на i
 
pi = 3;
cout << i;     // i == 3

Пример 2:

void double_numb(int &numb) {
	numb *= numb; // 'numb' - это тот же 'x' в функции main() 
}
void main(){
int x = 2;
	double_numb(x);
	cout << x << '\n';
	system("pause");
}

Пример 3. Передача аргумента по ссылке:

void calcSquareAndPerimeter(double a, double b, double &S, double &P) {
    S = a * b;
    P = 2 * (a + b);
}
 
int main() {
    double SS = 0.0, PP = 0.0;
    calcSquareAndPerimeter(3.0, 4.0, SS, PP);
    cout << "Perimeter: " << PP << ", Square: " << SS << "\n";
}

Пример 4. Передача аргумента по ссылке:

int a = 5;
int& ra = a;
ra = 3;
void swap( int & a , int & b ) // Передача по ссылке
{
 auto v = a;
 a = b;
 b = v;
}
int main()
{
 int c, d;
 swap (c, d);
}

Лабораторные работы

Все задания, которые вы не успели выполнить на уроке, автоматически становятся вашим домашним заданием. Домашнее задание должно быть выполнено до следующего урока.
  • Создайте приложение с именем Lesson_3. Все задания и лабораторные работы должны выполняться в этом приложении.
  • Это означает, что все задания должны размещаться в одном и том же заголовочном и главном файле. Необходимо размещать комментарии с номером задания и объяснением того, что нужно сделать в нем.
  • Каждая задача должна выполняться с использованием пользовательских функций. Например, если задача состоит в том, чтобы найти сумму последовательности из 5 введенных чисел, вы должны создать функцию в файле .cpp для выполнения этой задачи:
  • header.h:

    1
    2
    3
    4
    5
    6
    
    #ifndef HEADER_H
    #define HEADER_H
    // Задание 1
    // функция расчета суммы 5 введенных чисел
    int sumSeq();
    #endif

    imp.cpp:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    #include <iostream>
    #include "header.h"
    using namespace std;
     
    // функция расчета суммы 5 введенных чисел
    int sumSeq() {
    	cout << "enter a sequence"<<endl;
    	int numb;
    	int sum = 0;
    	for (int i = 0; i < 5; i++) {
    		cin >> numb;
    		sum += numb;
    	}
    	return sum;
    }

    main.cpp:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    #include "header.h"
    #include <iostream>
    using namespace std;
    void main(){
            // задание # 1
            // Создать функцию расчета суммы 5 введенных чисел
    	int result = sumSeq();
            cout << "задание 1" << endl;
    	cout << result;
    	system("pause");
    }
  • Старайтесь передавать аргументы функций по ссылке (в тех задачах, где это необходимо).

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

Лабораторная работа №1:
Описать две функции для обмена значениями двух переменных, соответственно, целого и вещественного типа.
 
* Я. М. Демяненко, М. И. Чердынцева «Методы процедурного программирования в с++» учебное пособие, Ростов-на-дону, 2014, стр. 18

Выполнение:

  1. Создайте главный и заголовочный файлы. Подключите необходимые библиотеки и заголовочный файл.
  2. В код заголовочного файла добавьте описание и реализацию двух функций с одинаковым названием — my_swap(): для обмена значений переменных целого типа и для обмена значений переменных вещественного типа:
  3. // объявление функций
    void my_swap(double &a, double &b);
    void my_swap(int &a, int &b);
     
    // определение (реализация) функций
    void my_swap(double &a, double &b) {
     double r = a;
     a=b;
     b=r;
    }
    void my_swap(int &a, int &b) {
     int r=a;
     a=b;
     b=r;
    }
    • Объявление двух функций с одинаковыми именами означает, что имеет место перегрузка имени функции.
    • Перегрузка функции — возможность определить несколько функций с одним и тем же именем.
    • Перегруженные функции обязательно должны иметь разные сигнатуры, т. е. должны отличаться своими списками спецификации параметров.

    В объявлении и определении функций используется передача параметров по ссылке, о чем указывает символ &. Ссылочные переменные позволяют сохранять результат изменения значений формальных параметров таким образом, чтобы он был «виден» в точке их вызова.

  4. В код главного файла в тело функции main() добавьте описание и ввод необходимых переменных, а также вызов функций:
  5. int main() {
     int k,m;
     cout<<"введите два целочисленных значения:";
     cin>>k>>m;
     cout<<k<<" "<<m<<endl;
     my_swap(k, m);
     cout<<k<<" "<<m<<endl;
     double a,b;
     cout<<"введите два вещественных значения:";
     cin>>a>>b;
     cout<<a<<" "<<b<<endl;
     my_swap(a,b);
     cout<<a<<" "<<b<<endl;
     
     system("pause");
     return 0;
    }
    • В операторе вызова функции my_swap(k,m) в качестве фактических параметров используются две целые переменные k и m. В таком случае компилятор вызовет ту версию перегруженной функции, у которой параметрами являются целые переменные.
    • Оператор вызова функции my_swap(a,b) использует два фактических параметра вещественного типа. Для него будет осуществлен вызов функции с двумя вещественными параметрами.
Лабораторная работа № 2:

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

Указания:

  • Использовать механизм встраиваемых (inline) функций
  •  
    * Я. М. Демяненко, М. И. Чердынцева «Методы процедурного программирования в с++» учебное пособие, Ростов-на-дону, 2014, стр. 25

    Выполнение:

    1. Создайте главный и заголовочный файлы. Подключите необходимые библиотеки и заголовочный файл.
    2. В код заголовочного файла добавьте описание и реализацию трех функций с одинаковым названием — max(), но с разным количеством параметров и их типом (объявление функций добавьте самостоятельно, пользуясь кодом их определения):
    3. inline int max(const int a, const int b) {
        return a>b?a:b;
      }
      inline int max(const int a, const int b, const int c) {
        int d=a>b?a:b;
        return d>c?d:c;
      }
      inline double max(const double a, const double b) {
        return a>b?a:b;
      }
    4. Вызовы функций порой приводят к снижению производительности программы. В C++ для минимизации такого эффекта предусмотрен механизм встраиваемых (inline) функций, которые иногда еще называют подставляемыми.
    5. Спецификация inline перед указанием типа результата в объявлении предлагает компилятору сгенерировать копию кода функции в соответствующем месте, чтобы избежать вызова этой функции. В результате получается множество копий кода функции, вставленных в программу, вместо единственного экземпляра кода, которому передается управление при каждом вызове функции.
    Задание 1:

    • Аргументы по умолчанию — значения, которые автоматически подставляются компилятором при вызове функции, если аргумент не был указан при вызове.
    • Аргументы по умолчанию могут быть расположены только в конце списка формальных параметров.
    int max(int a, int b = 1);
    Задание 2:
    Создать проект с функцией сортировки трех значений с возможностью задать направление сортировки. Реализовать тестирующую программу.
    Реализовать перегруженные функции для сортировки нескольких значений (двух, четырех), возможно, разных типов.
    Подробно:
    Создать функцию:

    void sort ( double &a, double &b, double &c, bool asc = 0);

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

    Параметры функции:
    a, b, c – значения для сортировки.
    asc — флаг направления сортировки:
    false — по убыванию (по умолчанию),
    true — по возрастанию.

    Возвращаемое значение: нет

    Результат:
    a, b, c отсортированные в соответствии со значением asc.

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

    Указания:
    Оформить в виде функций каждое из действий:

  • определение количества цифр в десятичной записи числа:
  • int count_dig(int a); //параметр передается по значению
  • вычисление суммы цифр числа:
  • int sum_dig(int a); //параметр передается по значению
  • преобразование числа путем вычеркивания из него первой и последней цифры:
  • void del_last_dig(int& a); //параметр передается по ссылке
    void del_first_dig(int& a); //параметр передается по ссылке

     
    * Я. М. Демяненко, М. И. Чердынцева «Методы процедурного программирования в с++» учебное пособие, Ростов-на-дону, 2014, стр. 29

    For loop:
    Задание 4:

    Выполнить: Создайте функцию для вывода последовательности: -3 0 3 6 9 12 15 18 21 24 и суммы ее элементов. Выполните задачу с помощью цикла FOR. Итератор цикла с шагом, равным 3.

    Указание 1: Чтобы сделать шаг равным 3, используйте синтаксис:

    for ([initializers]; [condition]; [iterator]) 

    вместо итератора у вас будет переменная counter += 3

    Указание 2: Сигнатура функции должна быть void printSeq(int a, int b, int & sum), где a = -3 и b = 24.

    Указание 3: Не забудьте использовать оператор assert, чтобы проверить, больше ли b, чем a.

    #include <cassert>

    Примерный вывод:

    Последовательность: -3 0 3 6 9 12 15 18 21 24
    сумма: 105
    

    [Solution and Project name: Lesson_3 файлы: header.h, imp.cpp, main.cpp]
    // задание 4

    or files: L3Task1header.h, L3Task1imp.cpp, L3Task1main.cpp
    Задание 5:

    Выполнить: Вводится 10 целых чисел. Создайте функцию (findPosNeg) для вывода количества положительных и отрицательных значений среди них.

    Указание 1: Создайте цикл for для ввода чисел. Внутри цикла проверьте каждое число, является ли оно положительным или отрицательным. Используйте два счетчика.

    Указание 2: сигнатура функции:

    void findPosNeg(int & pos, int & neg){
      //TODO
    }

    Примерный вывод:

    введите 10 чисел:
    1  -5  -12   2   3   9   -1  9   5   -8    
    counter_positive = 6, counter_negative = 4

    [Solution and Project name: Lesson_3 ]
    // Задание 5

    Задание 6:

    Выполнить: Создайте функцию для вычисления суммы следующей последовательности: 1 + 3 + 5 + 7 + 9 + 11 + 13 + 15 + 17 + 19 (числа НЕ вводятся, они должны быть созданы с помощью цикла). Границы последовательности должны быть параметрами функции, сумма — также параметр функции, передаваемый по ссылке.

    Примерный вывод:

    последовательность:
    1 + 3 + 5 + 7 + 9 + 11 + 13 + 15 + 17 + 19   
    sum = 100
    

    [Solution and Project name: Lesson_3 ]
    // Задание 6

    Задание 7:

    Выполнить: Для каждого x, изменяющегося в интервале [x1;x2] (значения x1 и x2 вводятся), вычислите значение функции z(x,y) = xy. Переменная y изменяется в интервале [y1;y2] (значения y1 и y2 вводятся). Вы должны создать функцию.

    Указание 1: Создайте два цикла for (вложенный цикл): один цикл внутри другого. Переменная x должна быть изменена во внешнем цикле; переменная y должна быть изменена во внутреннем цикле.

    Указание 2: Чтобы вывести степень числа, используйте функцию pow директиву #include<cmath>:

    f = pow(base number, exponent);

    Указание 3: Не забудьте использовать оператор assert для проверки значений (x2>x1 и y2>y1).

    Примерный вывод:

    введите x1 и x2:
    >>>2 >>>8
    введите y1 и y2:
    >>>2 >>>4
    z(x,y) = 2^2 = 4
    z(x,y) = 2^3 = 8
    z(x,y) = 2^4 = 16
    z(x,y) = 3^2 = 9
    z(x,y) = 3^3 = 27
    z(x,y) = 3^4 = 81
    z(x,y) = 4^2 = 16
    z(x,y) = 4^3 = 64
    z(x,y) = 4^4 = 256
    z(x,y) = 5^2 = 25
    z(x,y) = 5^3 = 125
    z(x,y) = 5^4 = 625
    ... и т.д.

    [Solution and Project name: Lesson_3 ]
    // task 7

    While loop:
    Задание 8:

    Выполнить: Создайте функцию для вывода последовательности 15 12 9 6 3 0 (от 15 до 0 с шагом = -3) и суммы четных элементов последовательности. Используйте цикл while.

    Указание: Сигнатура функции должна быть:

    void printSeqTask5(int a, int b, int & sum){}

    Где a = 15, а b = 0

    Примерный вывод:

    результат:
    15 12 9 6 3 0
    сумма четных = 18
    

    [Solution and Project name: Lesson_3 ]
    // задание 8

    Задание 9:

    Выполнить: Создайте функцию для вычисления произведения 2-значных четных чисел в интервале [10;20] (10 * 12 * 14 * 16 * 18 * 20). Вы должны выполнить задачу, используя цикл while.

    Указание 1: Для вычисления произведения следует использовать переменную с именем product. Не забудьте инициализировать переменную product значением 1.

    Указание 2: Сигнатура функции:

    void findMult(int a, int b, int & prod){}

    где a = 10 и b = 20

    Примерный вывод:

    10 * 12 * 14 * 16 * 18 * 20  
    произведение: 9676800

    [Solution and Project name: Lesson_3 ]
    // задание 9

    Задание 10:

    Выполнить: Вводятся два двузначных целых числа. Создайте функцию BitwiseSum(), которая вычисляет их побитовую сумму по модулю 10. Например, побитовая сумма чисел 34 и 59 — это число 83 (3 + 5 = 8; 4 + 9 = 13; 13%10 = 3).

    Указание: Функция BitwiseSum() должна принимать три аргумента по ссылке (два числа и sum).

    Примерный вывод:

    Введите два двухзначных числа:
    >>> 34   >>> 59
    Побитовая сумма 34 и 59 = 83

    [Solution and Project name: Lesson_3 ]
    // задание 10

    Задание 11:

    Выполнить: Задается последовательность целых чисел (вводится). Последний элемент последовательности равен 0. Вычислите сложение всех положительных элементов этой последовательности и найдите количество ее отрицательных элементов. Необходимо использовать цикл While.

    Указание 1: В задаче вы должны использовать бесконечный цикл с выходом из середины:

    while (true)
    	{
    		// TODO something
    		if (num == 0)
    			break;
     
                    // TODO something
            }

    Указание 2: Чтобы вернуть значения из функции, вы должны использовать параметры по ссылке (&):

    void sumPosCountNeg(int & pos_sum, int & neg) {}

    Примерный вывод:

    Введите последовательность, закончите ввод 0:
    >>> 3   5   -1   2  -3   0
    Сумма положительных: 10
    Кол-во отрицательных: 2
    

    [Solution and Project name: Lesson_3 ]
    // задание 11

    Задание 12:

    Выполнить: Задается последовательность целых чисел (вводится). Последний элемент последовательности равен 0. Найдите минимальный и максимальный элементы. Необходимо использовать цикл While.

    Указание 1: В задаче вы должны использовать бесконечный цикл с выходом из середины:

    while (true)
    	{
    		// TODO something
    		if (num == 0)
    			break;
                    // TODO something
                    //
            }

    Указание 2: Чтобы вернуть значения из функции, вы должны использовать параметры по ссылке (&):

    void minMax(int &min, int &max) {}

    Указание 3: Следует использовать начальные значения INT32_MAX и INT32_MIN для переменных min и max.

    Примерный вывод:

    Введите последовательность, закончите ввод 0:
    >>> 3   5   -1   2  -3   0
    minimum : -3
    maximum : 5
    

    [Solution and Project name: Lesson_3 ]
    // задание 12

    EXTRA TASKS

    ExtraЗадание 1 (while loop):

    Выполнить: Задается целое число. Найдите количество его цифр и их сумму.

    Указание: Сигнатура функции:

    void digitsCountAndSum (int n, int& count, int& sum)

    Примерный вывод:

    Введите число: 
    >>> 12345
    Кол-во цифр = 5, сумма = 15

    [Solution and Project name: Lesson_3, files: L3ExTask1header.h, L3ExTask1main.cpp, L3ExTask1imp.cpp]

    ExtraЗадание 2 (аргументы по ссылке):

    Выполнить: Задано целое число N и набор из N положительных вещественных чисел. Вычислите сумму всех целых частей чисел последовательности, а также сумму всех дробных частей чисел последовательности.

    Примерный вывод:

    Введите N: 
    >>> 4
    12,4  11,7  8,5  1,1
    Сумма целых частей: 32 
    Сумма дробных частей: 1.7

    [Solution and Project name: Lesson_3, files: L3ExTask2header.h, L3ExTask2main.cpp, L3ExTask2imp.cpp]

    Домашнее задание

    For loop

    Д.з. 1:

    Выполнить: Вводятся 10 действительных чисел. Создайте функцию для вывода произведения введенных чисел.
    Указание 1: Создайте цикл for для ввода чисел. Чтобы вычислить умножение, используйте переменную с именем product.
    Указание 2: Не забудьте инициализировать переменную product значением типа double.

    double product = 1.0; 

    Примерный вывод:

    1,1  2,4  5,1  7,2  6,4  8,1  6,7  3,2  3,3  2,4  
    product = 853338,921998746

    [Solution and Project name: Lesson_3, files: L3HomeTask1header.h, L3HomeTask1main.cpp, L3HomeTask1imp.cpp]

    Д.з. 2:

    Выполнить: Для каждого x, изменяющегося в интервале [30;33], вычислите значение функции z(x,y) = x - y. Переменная y изменяется в интервале [1;5]. Вы должны создать функцию.

    Указание: Создайте два цикла for (вложенный цикл): один цикл внутри другого. Переменная x должна быть изменена во внешнем цикле; переменная y должна быть изменена во внутреннем цикле.

    Примерный вывод:

    z(x,y) = 30-1=29
    z(x,y) = 30-2=28
    z(x,y) = 30-3=27
    z(x,y) = 30-4=26
    z(x,y) = 30-5=25
    z(x,y) = 31-1=30
    ... и т.д.

    [Solution and Project name: Lesson_3, files: L3HomeTask2header.h, L3HomeTask2main.cpp, L3HomeTask2imp.cpp]

    While loop
    Д.з. 3:

    Выполнить: Создайте функцию для вывода последовательности 3 5 7 9 ... 21 (от 3 до 21 с шагом = 2) и количества элементов кратных трем.

    Указание: Сигнатура функции должна быть void seq(int a, int b, int & counter), где a = 3 и b = 21.

    Примерный вывод:

    3 5 7 9 11 13 15 17 19 21
    кратных трем: 4
    

    [Solution and Project name: Lesson_3, files: L3HomeTask3header.h, L3HomeTask3main.cpp, L3HomeTask3imp.cpp]

    Д.з. 4:

    Выполнить: Вводятся 5 действительных чисел. Создайте функцию для вычисления суммы введенных чисел. Выполните задачу, используя цикл while.

    Указание: Double тип должен использоваться для действительных чисел и для переменной суммы:

    double sum = 0;
    double numb;

    Please enter 5 numbers and press Enter

    1.1 4.2 2.1 9.3 2.5
    сумма = 19.2

    [Solution and Project name: Lesson_3, files: L3HomeTask4header.h, L3HomeTask4main.cpp, L3HomeTask4imp.cpp]

    Задания для самостоятельного выполнения:

    Указания: задания следует сохранить в файле с именем задания, и обязательно в коде вставить комментарий с постановкой задачи.

    Рекурсия

    1. rec1: Напишите рекурсивную функцию, которая раскладывает число на простые сомножители.
    2. Пример:

      Введите натуральное число:
      378
      378 = 2*3*3*3*7
    3. rec2: Дано натуральное число N. Требуется получить и вывести на экран количество всех возможных различных способов представления этого числа в виде суммы натуральных чисел (то есть, 1 + 2 и 2 + 1 – это один и тот же способ разложения числа 3). Решите задачу с помощью рекурсивной процедуры.
    4. Пример:

      Введите натуральное число:
      4
      Количество разложений: 4.