V Flashcards

1
Q

Функция, которая ничего не возвращает

A
  • При создании функции, которая не должна возвращать никаких значений, тип возвращаемого ею значения указывается как void (пустота).
  • Если функция не объявлена с типом возвращаемого значения void, в теле функции необходимо нали­чие оператора return .
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Параметры функций со значениями по умолчанию

A
  • До сих пор мы использовали в примерах фиксированное значение числа π как кон­станту, не предоставляя пользователю возможности его изменить. Однако пользовате­ля функции может интересовать более точное или менее точное ее значение. Как при создании функции, использующей число π, позволить ее пользователю использовать собственное значение, а при его отсутствии задействовать стандартное?
  • Один из способов решения этой проблемы подразумевает создание в функции Area ( ) дополнительного параметра для числа π и присваивание ему значения но умолчанию (default value). Такая адаптация функции Area( ) выгляде­ла бы следующим образом:
  • double Area(double radius, double Pi = 3.14);*
  • Обратите внимание на второй параметр Pi и присвоенное ему по умолчанию значение 3,14. Этот второй параметр является теперь необязательным параметром (optional parameter) для вызывающей функции. Вызывающая функция все равно мо­жет вызвать функцию Area (), используя синтаксис
  • Area(radius);*
  • В данном случае второй параметр был проигнорирован, поэтому используется его значение по умолчанию 3,14. Но если пользователь захочет задействовать другое зна­чение числа π, то можно сделать это, вызвав функцию Area ( ) следующим образом:
  • Area (radius, Pi); // Pi определяется пользователем*
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Рекурсивная функция

A
  • В некоторых случаях функция может фактически вызывать сама себя. Такая функ­ция называется рекурсивной (recursive function). Обратите внимание, что у рекурсив­ной функции должно быть четко определенное условие выхода, когда она завершает работу и больше себя не вызывает.
  • При отсутствии условия выхода или при ошибке в нем выполнение про­граммы застрянет в рекурсивном вызове функции, которая непрерывно будет вызывать сама себя, пока в конечном счете не приведет к перепол­нению стека и аварийному завершению приложения.
  • Рекурсивные функции могут пригодиться при вычислении чисел ряда Фибоначчи: значение каждого следующего числа последовательности — это сумма двух предыдущих чисел. Таким образом, n-е значение последовательности (при п> 1) определяется следующей (рекурсивной) формулой:

Fibonacci (n) = Fibonacci (n - 1) + Fibonacci (n - 2)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Функции с несколькими операторами return

A
  • Вы не ограничены наличием только одного оператора return в определении функ­ции. По желанию вы можете осуществлять выход из функции в любом месте, не обя­зательно только в одном. В зависимости от логики и задачи приложения это может быть и преимуществом, и недостатком.
  • Используйте несколько выходов из функции осторожно. Значительно про­ще исследовать и понять функцию, которая начинается вверху и заканчи­вается внизу, чем функцию, которая имеет несколько выходов в разных местах.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

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

A
  • Функции с одинаковым именем и одинаковым типом возвращаемого значения, но с разными наборами параметров называют перегруженными функциями (overloaded function).
  • Перегруженные функции могут быть весьма полезными, например, в при­ложениях, в которых имеется функция с определенным именем, которая осуществляет некоторый вывод, но может быть вызвана с различными наборами параметров. Пред­положим, необходимо написать приложение, которое вычисляет площадь круга и площадь поверхности цилиндра. Функция, которая вычисляет площадь круга, нуждается в одном параметре — радиусе. Вторая функция, которая вычисляет площадь поверх­ности цилиндра, нуждается, кроме радиуса, во втором параметре — высоте цилиндра. Обе эти функции должны возвратить данные одного типа, содержащие площадь.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Передача в функцию массива значений

A

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

void Displaylnteger (int number);

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

void Displaylntegers( int[] numbers, int length);

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Передача аргументов по ссылке

A

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

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Как процессор обрабатывает вызовы функций

A
  • Вызов функции, по существу, означает, что процессор переходит к выполнению следующей команды, принадлежащей вызываемой функции и расположенной в не­которой не последовательной области памяти. После выполнения команд функции поток выполнения возвращается туда, откуда был совершен переход в функцию. Для реализации этой логики компилятор преобразует вызов функции в команду процес­сора CALL. Данная команда включает адрес следующей команды для выполнения (это адрес команды вызываемой функции). Когда процессор встречает команду CALL, он сохраняет в стеке позицию команды, которая будет выполнена после возврата из функ­ции, и переходит к командам в области памяти, указанной в команде CALL.
  • Эта область памяти содержит команды, принадлежащие функции. Процессор вы­полняет их до тех пор, пока не встретит команду RET (машинный код для оператора return в программе C++). Команда RET требует от процессора извлечь из стека адрес, сохраненный во время выполнения команды CALL, и использовать его в качестве адреса команды в вызывающей функции, которой должно продолжиться выполнение про­граммы. Таким образом, процессор возвращает выполнение вызывающей функции, и оно продолжается с того места, где было прервано вызовом функции.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Понятие стека

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Встраиваемые функции

A
  • Вызов обычной функции преобразуется в команду CALL, которая приводит к вы­полнению операций со стеком, переходу процессора к выполнению кода функции и т.д. Эта дополнительная работа, выполняемая невидимо для пользователя, в большин­стве случаев невелика. Но что если функция очень проста, как эта?
  • double GetPi( )*
  • {*
  • return 3.14159;*
  • }*
  • Накладные расходы времени на выполнение фактического вызова функции в этом случае весьма высоки по сравнению со временем, затраченным на выполнение кода функции GetPi( ). Вот почему компиляторы C++ позволяют программисту объявлять такие функции как встраиваемые (inline). Ключевое слово inline — это просьба встроить реализацию функции вместо ее вызова в место ее вызова.
  • inline double GetPi( )*
  • {*
  • return 3.14159;*
  • }*
  • Большинство современных компиляторов C++ оснащены высококачествен­ными оптимизаторами кода. Многие из них позволяют оптимизировать программу по размеру, создавая приложение минимального размера, или по скорости, обеспечивая максимальную его производительность. Первое весьма важно при разработке программного обеспечения для различных устройств наподобие мобильных, в которых не так уж много памяти. При такой оптимизации компилятор отклоняет большинство просьб о встраива­нии, поскольку это может увеличить размер кода.При оптимизации по скорости компилятор обычно удовлетворяет прось­бы о встраивании (там, где это имеет смысл), причем делает это зачастую даже в тех случаях, когда никто его об этом не просит.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Автоматический вывод возвращаемого типа

A
  • Вместо указания типа возвращаемого значения можно использовать ключевое слово auto и позволить компилятору самому вывести тип возвращаемого значения на основе вашего кода.
  • Функции с использованием автоматического вывода типа возвращаемого значения должны быть определены (т.е. реализованы) до того, как вы бу­дете к ним обращаться. Это связано с тем, что компилятор должен знать тип возвращаемого значения функции в точке, где она используется. Если такая функция содержит несколько операторов return, все они должны возвращать один и тот же тип. Рекурсивные вызовы должны предваряться по крайней мере одним оператором return в теле функции.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Лямбда-функции

A

Лямбда-функции введены стандартом С++11 и очень помогают использовать ал­горитмы STL для сортировки и обработки данных. Как правило, функция сортировки требует бинарного предиката, который представляет собой функцию, сравнивающую два аргумента и возвращающую true, если первый меньше второго, и false в про­тивном случае (тем самым определяя порядок, в котором должны находиться отсорти­рованные элементы). Такие предикаты обычно реализуются в виде операторов класса, что требует весьма кропотливого программирования.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Почему бы не встраивать каждую функцию? Ведь это увеличит скорость вы­полнения, не так ли?

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

У меня есть две функции, обе по имени Area. Одна получает радиус, а дру­гая высоту. Я хочу, чтобы одна возвращала тип float, а другая тип double. Это возможно?

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Что такое указатель?

A
  • Не усложняя, можно сказать, что указатель (pointer) — это переменная, которая хранит адрес области в памяти. Точно так же, как переменная типа int используется для хранения целочисленного значения, переменная указателя используется для хра­нения адреса области памяти
  • Таким образом, указатель — это переменная, и, как и все переменные, он зани­мает пространство в памяти (в случае рис. — по адресу 0x101). Но особенными указатели делает то, что содержащиеся в них значения (в данном случае — 0x558) интерпретируются как адреса областей памяти. Следовательно, указатель — это спе­циальная переменная, которая указывает на область в памяти.
  • Адреса памяти обычно представлены в шестнадцатеричной записи. Это си­стема счисления с основанием 16, т.е. использующая 16 различных симво­лов - за 0-9 следуют символы A-F. По соглашению перед шестнадцатерич­ным числом записывается префикс Ох. Таким образом, шестнадцатеричное число ОхА представляет собой 10 в десятичной системе счисления; шест­надцатеричное OxF - 15; а шестнадцатеричное 0x10 - 16.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Объявление указателя

A
  • Поскольку указатель является переменной, его следует объявить, как и любую иную переменную. Обычно вы объявляете, что указатель указывает на значение определенного типа (например, типа int). Это значит, что содержавшийся в указа­теле адрес указывает на область в памяти, содержащую целое число. Можно опреде­лить указатель и на нетипизированный блок памяти (называемый также указателем на void). Указатель должен быть объявлен, как и все остальные переменные:
  • Указываемый_тип * Имя_Переменной_Указателя;*
  • Как и в случае с большинством переменных, если не инициализировать указа­тель, он будет содержать случайное значение. Во избежание обращения к случайной области памяти указатель инициализируют значением nullptr (это значение — нулевой указатель — появилось в языке C++ в стандарте С++11.). Значение указате­ля всегда можно проверить на равенство значению nullptr, которое не может быть адресом реальной области памяти:
  • Указываемый_тип * Имя_Переменной_Указателя = nullptr;*
  • Таким образом, объявление указателя на целое число может иметь следующий вид:
  • int *plnteger = nullptr;*
  • Указатель, как и переменная любого другого изученного на настоящий мо­мент типа данных, до инициализации содержит случайное значение. В слу­чае указателя это случайное значение особенно опасно, поскольку озна­чает некоторый адрес области памяти. Неинициализированные указатели способны заставить вашу программу обратиться к недопустимой области памяти, приводя (в лучшем случае) к аварийному завершению.
17
Q

Определение адреса переменной с использованием оператора получения адреса &

A

Если varName — переменная, то выражение &varName возвращает адрес места в памяти, где хранится ее значение.

Так, если вы объявили целочисленную переменную, используя хорошо знакомый вам синтаксис

int age = 30;

то выражение &аgе вернет адрес области памяти, в которую помещается указанное значение 30.

Оператор получения адреса & иногда называют также оператором ссылки (referencing operator).

18
Q

Использование указателей для хранения адресов

A
  • // Объявление переменной*
  • Тип Имя_Переменной = Начальное_Значение;*

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

  • // Объявление указателя на тот же тип и его инициализация*
  • Тип* Указатель = &Имя_Переменной;*
19
Q

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

A
  • Предположим, у вас есть указатель, содержащий вполне допустимый адрес. Как же теперь обратиться к этой области, чтобы записать или прочитать содержащиеся в ней данные? Для этого используется оператор разыменования (dereferencing operator) *. По существу, если есть корректный указатель pData, выражение *pData позволяет по­лучить доступ к значению, хранящемуся по адресу, cодержащемуся в этом указателе.
  • Оператор разыменования * называется также оператором косвенного об­ращения (indirection operator).
20
Q

Значение sizeof ( ) для указателя

A

Результат выполнения оператора sizeof ( ) для указателя зависит от компи­лятора и операционной системы, для которой программа была скомпилирована, и не зависит от характера данных, на которые он указывает.

21
Q

Динамическое распределение памяти

A

Когда вы пишете программу, содержащую объявление массива, такое как

int Numbers[100]; // Статический массив для 100 целых чисел

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

  1. Вы фактически ограничиваете возможности своей программы, поскольку она не сможет хранить больше 100 чисел.
  2. Вы неэффективно используете ресурсы в случае, когда храниться должно, ска­жем, только 1 число, а память все равно выделяется для 100 чисел.

Причиной этих проблем является статическое, фиксированное выделение памяти для массива компилятором.

Чтобы программа могла оптимально использовать память, в зависимости от кон­кретных потребностей пользователя, необходимо использовать динамическое рас­пределение памяти. Оно позволяет при необходимости выделять большее количество памяти и освобождать ее, когда необходимости в ней больше нет. Язык C++ предо­ставляет два оператора, new и delete , позволяющие управлять использованием памя­ти в вашем приложении. В эффективном динамическом распределении памяти крити­чески важную роль играют указатели, хранящие адреса памяти.

22
Q

Использование new и delete для выделения и освобождения памяти

A
  • Оператор new используется для выделения новых блоков памяти. Чаще всего ис­пользуется версия оператора new, возвращающая указатель на затребованную область памяти в случае успеха и генерирующая исключение в противном случае. При ис­пользовании оператора new необходимо указать тип данных, для которого выделяется память:
  • Тип* Указатель = new Тип; // Запрос памяти для одного элемента*

Вы можете также определить количество элементов, для которых хотите выделить память (если нужно выделять память для массива элементов):

Тип* Указатель = new Тип[Количество]; // Запрос памяти для указан

// ного количества элементов

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

  • int* pointToAnlnt = new int; // Указатель на целое число*
  • int* pointToNums = new int[10]; // Указатель на массив из 10*
  • // целых чисел*
  • Обратите внимание на то, что оператор new запрашивает область памяти. Нет никакой гарантии, что запрос всегда будет удовлетворен успешно, по­скольку это зависит от состояния системы и доступного количества памяти.

Каждая область памяти, выделенная оператором new, должна быть в конечном сче­те освобождена соответствующим оператором delete:

  • Тип* Указатель = new Тип;*
  • delete Указатель; // Освобождение памяти, выделенной*
  • // ранее для одного экземпляра Типа*

Это справедливо и при запросе памяти для нескольких элементов:

  • Тип* Указатель = new Тип [Количество] ;*
  • delete[] Указатель; // освободить выделенный ранее массив*
  • Обратите внимание на применение оператора delete [] при выделении блока с использованием оператора new [. . .] и оператора delete при выделении только одного элемента с использованием оператора new.
  • Если не освободить выделенную память по окончании ее использования, она оста­нется выделенной и недоступной для последующих выделений вашему или иным приложениям. Такая утечка памяти может привести даже к замедлению работы при­ложения или компьютера в целом, и ее следует избегать любой ценой.
  • Операторы new и delete выделяют область в динамической памяти. Динамическая память (free store) - это абстракция памяти в форме пула памяти, из который диспетчер памяти может выделять блоки памяти для ва­шего приложения и освобождать их, возвращая в пул свободной памяти.
23
Q

Использование ключевого слова const с указателями

A

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

  • Содержащийся в указателе адрес является константным и не может быть изменен, однако данные, на которые он указывает, вполне могут быть изменены:
  • int daysInMonth = 30;*
  • int* const pDaysInMonth = &daysInMonth;*
  • *pDaysInMonth = 31; // OK! Значение может бьггь изменено*
  • int daysInLunarMonth = 28;*
  • pDaysInMonth = &daysInLunarMonth; // Ошибка компиляции: нельзя*
  • // изменить адрес!*
  • Данные, на которые указывает указатель, являются константными и не могут быть изменены, но сам адрес, содержащийся в указателе, вполне может быть изменен (т.е. указатель может указывать и на другое место):
  • int hoursInDay = 24;*
  • const int* pointsToInt = &hoursInDay;*
  • int monthsInYear = 12;*
  • pointsToInt = &monthsInYear; //OK!*
  • *pointsToInt = 13; // Ошибка времени компиляции: изменять данные нельзя*
  • int* newPointer = pointsToInt; //Ошибка времени компиляции: нельзя присваивать указатель на константу указателю на не константу*
  • И содержащийся в указателе адрес, и значение, на которое он указывает, являются константами и не могут быть изменены (самый ограничивающий вариант):
int hoursInDay = 24;
const int\* const pHoursInDay = &HoursInDay;
\*pHoursInDay = 25; // Ошибка компиляции: нельзя изменять значение, на которое указывает данный указатель 
int daysInMonth = 30;
pHoursInDay = SdaysInMonth; // Ошибка компиляции: нельзя изменять значение данного указателя
  • Эти разнообразные формы константности особенно полезны при передаче указа­телей в функции. Параметры функций следует объявлять, обеспечивая максимально ограничивающий уровень константности, чтобы гарантировать невозможность функ­ции изменить значение, на которое указывает указатель, если таковое изменение не предполагается в данной функции. Это предотвратит возможность допущения про­граммистом ошибочного изменения значения указателя или данных, на которые он указывает.
24
Q

Передача указателей в функции

A

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

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

25
Q

Сходство между массивами и указателями

A
  • Массив — это указатель на его первый элемент.
  • Поскольку переменные массивов, по существу, являются указателями, при работе с массивами вполне можно использовать оператор разыменования *, как при работе с обычными указателями.
  • Объявление массива подобно созданию указателя для работы в пределах фик­сированного диапазона памяти.
  • Не забывайте, что указатели, созданные динамически с помощью операто­ра new, следует освободить с помощью оператора delete , даже если вы использовали для обращения к данным тот же синтаксис, что и для стати­ческих массивов. Если вы забудете об этом, то произойдет утечка памяти, а это плохо.
26
Q

Наиболее распространенные ошибки при использовании указателей

A

1. Утечки памяти

Вероятно, это одна из самых распространенных проблем приложений C++: чем дольше они выполняются, тем больший объем памяти используют и тем самым за­медляют систему. Это, как правило, случается, когда программист не обеспечил осво­бождение памяти, выделенной динамически оператором new, с помощью вызова опе­ратора delete по завершении ее использования.

Обеспечение освобождения всей выделенной памяти — задача программиста, т.е. ваша. Вы должны следить, чтобы никогда не происходили некоторые вещи напо­добие показанных ниже:

  • int* pointToNums = new int[5]; // Выделение памяти*
  • // Использование указателя pointToNums*
  • ….*
  • // Освобождение памяти с помощью delete[] pointToNums не сделано*
  • ….*
  • // Очередное выделение и перезапись указателя*
  • pointToNums = new int[10]; // Утечка ранее выделенной памяти*

2. Когда указатели указывают на недопустимые области памяти

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

3. Висячие (беспризорные, дикие) указатели

  • Любой корректный указатель становится некорректным после того, как он осво­бождается оператором delete. Чтобы избежать этой проблемы, большинство программистов присваивают ука­зателю при инициализации и после его освобождения значение nullptr, а затем, ис­пользуя его, проверяют корректность указателя, прежде чем применить к нему опера­тор разыменования.

4. Проверка успешности запроса с использованием оператора new

  • До сих пор в нашем коде мы полагали, что оператор new всегда возвращает кор­ректный указатель на блок памяти. На самом деле оператор new, как правило, выпол­няется успешно, если только не был запрошен необычно большой объем памяти или если система не находится в столь критическом состоянии, что у нее не хватает памя­ти. Существуют приложения, которые должны запрашивать большие объемы памяти (например, приложения баз данных), да и вообще, не следует думать, что распределе­ние памяти всегда осуществляется успешно. Язык C++ предоставляет две возможнос­ти удостовериться в успешности выделения памяти. Основной способ, действующий по умолчанию, подразумевает генерацию исключенш std::bad_alloc при неудачном выделении памяти. Генерация исключения приводит к прерыванию выполнения при­ложения с сообщением об ошибке наподобие “unhandled exception” (необработанное исключение), если только вы не создали обработчик исключений (exception handler).
27
Q

Полезные советы по применению указателей

A
28
Q

Что такое ссылка?

A

Ссылка (reference) — это псевдоним переменной. При объявлении ссылки ее необ­ходимо инициализировать переменной. Таким образом, ссылочная переменная — это только другое средство доступа к данным, хранимым в переменной.

Для объявления ссылки используется символ &, как в следующем примере:

VarType original = Value;
VarType& referenceVariable = original;

29
Q

Зачем нужны ссылки

A
  • Ссылки позволяют работать с областью памяти, которой они инициализируются. Это делает ссылки особенно полезными при создании функций. Типичная функция объявля­ется так:
  • Возвращаемый_Тип Сделать__Нечто (Тип Параметр);*

Функция Сделать_Нечто() вызывается так:

  • Возвращаемый_Тип Результат = Сделать_Нечто (Аргумент);*
  • Приведенный выше код ведет к копированию аргумента в параметр, который за­тем используется функцией Сделать__Нечто (). Этап копирования может быть весьма продолжительным, если рассматриваемый Аргумент занимает много памяти. Ана­логично, когда функция Сделать_Нечто ( ) возвращает значение, оно копируется в Результат. Было бы хорошо, если бы можно было избежать этого копирования, по­зволяя функции работать непосредственно с данными в стеке вызывающей функции. Ссылки обеспечивают эту возможность.

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

Возвращаемый_Тип Сделать_Нечто (Тип& Параметр);

Вызывается функция так же, как и раньше:

  • Возвращаемый_Тип Результат = Сделать_Нечто (Аргумент) ;*
  • Функция, которая получает параметр как ссылку, может возвращать значение, используя ссылочные же параметры.
30
Q

Использование ключевого слова const со ссылками

A

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

int original = 30;
const int& constRef = original;
constRef =40; // Недопустимо: constRef не может
 // изменить значение в original
int& ref2 = constRef; // Недопустимо: ref2 не const
const int& constRef2 = constRef; // OK
31
Q

Передача аргументов в функции по ссылке

A
  • Одно из главных преимуществ ссылок в том, что они позволяют вызываемой функции работать с параметрами без копирования из вызывающей функции, что су­щественно увеличивает производительность. Но поскольку вызываемая функция ра­ботает с параметрами, расположенными непосредственно в стеке вызывающей функ­ции, зачастую важно гарантировать невозможность вызываемой функции изменить значение переменной в вызывающей функции. Ссылки, определенные как const, обеспечивают эту возможность.
  • Константные ссылки — мощный инстру­мент, предоставляемый языком C++ для входных параметров и гарантии того, чтопередаваемое по ссылке значение не может быть изменено вызываемой функцией. На первый взгляд, это может показаться тривиальным, но в коллективе разработчиков, где один программист пишет первую версию функции, второй ее дополняет, а третий исправляет или совершенствует, использование константных ссылок имеет важное значение для качества программы.
32
Q

У меня есть два указателя:

int* pointToAnlnt = new int;
int* pCopy = pointToAnlnt;

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

A

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

33
Q

Зачем передавать значения в функцию по ссылке?

A

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