Глава 8
Вверх
В программе 9.6 показана функция swap() , использующая в качестве аргументов
указатели , а в программе 9.7 -та же функция но с использованием ссылок.
Использовать функцию которая принимает в качестве параметроа ссылки легче,
да и в программе такая функция легче читается, но как вызывающей функции
определить , каким способом переданы параметры-по ссылке или по значению?
Будучи клиентом или пользователем функции swap(), программист должен быть
уверен в том, что функция swap() на самом деле изменит параметры.
Самое время вспомнить о прототипе функции, которому в данном контексте
нашлось еще одно применение. Изучив параметры, объявленные в прототипе
(который обычно находится в файле заголовка вместе с другими прототипами)
программист будет точно знать , что значения, принимаемые функцией swap(),
передаются как ссылки, следовательно обмен значениями произойдет должныи
образом.
Если бы функция swap() была функцией-членом класса, то оъявление этого
класса, тоже расположенное в файле заголовка, обязательно содержало бы
эту информацию.
В С++ клиенты классов и функций всю необходимую информацию черпают
из файлов заголовков. Этот файл выполняет роль интерфейса с классом
или функцией, действительная реализация которых скрыта от клиента.
Это позволяет программисту сосредоточиться на собственных проблемах
и использовать класс или функцию не вникая в подробности их работы.
Программист читает интерфейсы функций и классов, чтобы определить какие
возможности предоставляет компонент, какие параметры он принимает
и какие значения возвращает.
Вверх
Как уже говорилось функция может возвращать только одно значение.
Что делать если необходимо получить от функции сразу два значения?
Решением этой проблемы является передача функции по ссылкам двух объектов.
В ходе выполнения функция присвоит этим объектам нужные значения.
Передача объектов по ссылке, позволяющая функции изменить исходные объекты,
равносильна разрешению функции возвратить два значения. В этом случае можно
обойтись вообще без возвращаемого значения, которое(зачем же добру
пропадать ) можно использовать для сообщения об ошибках.
Здесь так же помогут ссылки и указатели.
В следующем примере показана функция, которая возвращает три значения:два
в виде параметров-указателей и одно в виде возвращаемого значения
функции.
//Listing 9.8
// Returning multiple values from a function
//using namespace std;
short Factor(int n, int* pSquared, int* pCubed);
int main()
cout << "Enter a number (0 - 20): ";
error = Factor(number, &squared, &cubed);
if (!error)
{
short Factor(int n, int *pSquared, int *pCubed)
{
Анализ:
Сначала объявляются три переменные number, squared, cubed. Переменной
number присваивается значение, введенное пользователем. Это значение
а так же адреса переменных squared, cubed передаются функции Factor()
в виде параметров.
В функции Factor() анализируется первый параметр , который передается как
значение. Если он больше 20 то возвращаемое значение функции value
устанавливается равным 1, что служит признаком ошибки. Функция Factor()
возвращает либо значение 1 либо 0, последнее свидетельствует об успешном
завершении.
Итак искомые значения -квадрат и куб заданного числа возвращаются
в вызывающую функцию не с помощью стандартного механизма возврата
значений, а за счет изменения значений переменных, указатели которых
переданы в функцию.
С помощью указателей этим переменным присваивается
результирующие значения.
А переменной value присваивается возвращаемое значение, свидетельствующее
об успешном завершении работы (значение 0). И функция возвращает это значение.
Задача функции была изменить значение двух переменных используя
указатели и вернуть значение переменной value свидетельствующей об успешном
или не успешном результате вычисления. Далее результат value используется
для вывода или не вывода на экран этих двух изменившихся переменных.
Возвращаемое значение функции Factor присваивается переменной error.
if (!error)
{
Эту программу можно слегка усовершенствовать, дополнив ее
следующим объявлением:
Чем возвращать малопонятные 0 и 1, лучше сразу применить
осмысленные значения SUCCES(успешно) и FAILURE(отказ).
Вверх
Хотя предыдущая программа вполне работоспособна, но если
вместо указателей применить ссылки, программа станет проще.
В следующей программе вместо указателей применены ссылки
и добавлено перечисление enum.
//Listing 9.9
// Returning multiple values from a function
// using references
typedef unsigned short USHORT;
ERR_CODE Factor(USHORT, USHORT&, USHORT&);
int main()
{
cout << "Enter a number (0 - 20): ";
result = Factor(number, squared, cubed);
if (result == SUCCESS)
ERR_CODE Factor(USHORT n, USHORT &rSquared, USHORT &rCubed)
{
Эта программа аналогична предыдущей за двумя исключениями. Перечисление
ERR_CODE делает сообщение об ошибке более осмысленным.
Однако наиболее существенно изменилась функция Factor() .
Теперь эта функция объявлена как принимающая не указатели, а ссылки на
переменные squared cubed, что упрощает работу с этими параметрами.
Вверх
При каждой передаче объекта в функцию как значения создается копия этого
объекта. При каждом возврате объекта из функции создается еще одна копия. Ранее
уже говорилось что эти объекты копируются в стек и на этот процесс расходуется
время и память. Для маленьких объектов цена этих расходов не значительна.
Кроме того существуют и другие затраты. При создании каждой из этих временных
копий, компилятор вызывает специальный конструктор-конструктор копий.
//Listing 9.10
// Passing pointers to objects
class SimpleCat
{
SimpleCat::SimpleCat()
{
SimpleCat::SimpleCat(SimpleCat&)
{
SimpleCat::~SimpleCat()
{
SimpleCat FunctionOne (SimpleCat theCat);
int main()
{
cout << "Calling FunctionOne...\n";
cout << "Calling FunctionTwo...\n";
return 0;
// FunctionOne, passes by value
// functionTwo, passes by reference
Результат:
Здесь объявляется весьма упрощенный класс SimpleCat.
Конструктор, конструктор копий, деструктор и все компоненты класса
выводят на экран свои информативные сообщения , чтобы было известно
когда они вызываются.
Обе объявленные функции тоже выводят на экран свои сообщения.
Глядя на прототипы функций мы видим что первая функция возвращает объект
типа SimpleCat и в параметре функции будет объект этого же самого типа.
В параметр этой функции мы ставим объект Frisky типа SimpleCat.
И функция вернет нам этот объект Frisky .
Вторая функция возвращает указатель на объект типа тоже SimpleCat,
на который указывает указатель.
В аргумент этой функции мы поставим адрес объявленного объекта
Frisky и указатель будет указывать на этот объект Frisky.
(Инициализируем указатель адресом объекта.)
Функция также нам вернет объект Frisky.
1. Сначала функция main() выводит первое сообщение.
Проанализировав эту программу можно сделать вывод:
что при вызове первой функции происходит два обращение к конструктору копий
и два обращения к деструктору.(поскольку объект в функцию передается как
значение)
И вот результат:
Как видим при вызове второй функции теперь конструктор и деструктор
вызываются один раз(а не два как при вызове первой функции), но когда
были использованы указатели во второй функции, то конструктор и деструктор
при вызове второй функции вообще не вызывались.
То есть ни каких копирующих конструкторов и деструкторов не было,
а теперь есть.
Назад |
Начало урока |
Вверх |
Вперед
Понятие заголовка и прототипа функции
Возвращение нескольких значений
#include <iostream.h>
{
short error;
cin >> number;
cout << "square: " << squared << "\n";
cout << "cubed: " << cubed << "\n";
else
cout << "Error encountered!!\n";
return 0;
if (n > 20)
Value = 1;
else
{
*pCubed = n*n*n;
Value = 0;
*pSquared = n*n;
*pCubed = n*n*n;
Value = 0;
Это или 1 или 2.
И далее оператор if(!error) я понимаю это выражение так:выражение в скобках
равно нулю. (если 0 то три сообщения, иначе сообщение об ошибке).
error = Factor(number, &squared, &cubed);
cout << "square: " << squared << "\n";
cout << "cubed: " << cubed << "\n";
else
cout << "Error encountered!!\n";
enum ERROR_VALUE(SUCCES, FAILURE);
Возвращение значений по ссылке
#include <iostream>
using namespace std;
enum ERR_CODE {
USHORT number, squared, cubed;
ERR_CODE result;
cin >> number;
{
cout << "number: " << number << "\n";
cout << "square: " << squared << "\n";
cout << "cubed: " << cubed << "\n";
else
cout << "Error encountered!!\n";
return 0;
return ERROR; // simple error code
else
{
rSquared = n*n;
rCubed = n*n*n;
return SUCCESS;
Передача ссылок как средство повышения эффективности
Конструктор копий вызывается каждый раз при помещении в стек временной копии
объекта.
При удалении временного объекта которое происходит при выходе из функции,
вызывается деструктор объекта. Если объект возвращается функцией как значение,
то копия этого объекта должна быть сначала создана а затем уничтожена.
Все это может оказать существенное влияние на скорость работы программы
и на расходование компьютерной памяти.
Посмотрите программу:
#include <iostream>
using namespace std;
SimpleCat (); // constructor
SimpleCat(SimpleCat&); // copy constructor
~SimpleCat(); // destructor
SimpleCat* FunctionTwo (SimpleCat *theCat);
cout << "Making a cat...\n";
SimpleCat Frisky;
FunctionOne(Frisky);
FunctionTwo(&Frisky);
SimpleCat FunctionOne(SimpleCat theCat)
{
return theCat;
SimpleCat* FunctionTwo (SimpleCat *theCat)
{
return theCat;
1.Making a cat...
Анализ:
2.Simple Cat Constructor...
//вызов первой функции
3.Calling FunctionOne...
4.Simple Cat Copy Constructor...
5.Function One. Returning...
6.Simple Cat Copy Constructor...
7.Simple Cat Destructor...
8.Simple Cat Destructor...
//вызов второй функции
9.Calling FunctionTwo...
10.Function Two. Returning...
11.Simple Cat Destructor...
SimpleCat FunctionOne (SimpleCat theCat);
SimpleCat* FunctionTwo (SimpleCat *theCat);
1. Making a cat...
2. Далее создается объект класса SimpleCat, это приводит к вызову конструктора.
Смотри второе сообщение.
2. Simple Cat Constructor...
3. Далее главная программа сообщает о вызове первой функции и вызывает ее.
3. Calling FunctionOne...
4. Поскольку при вызове функции FunctionOne() передача объекта класса SimpleCat
осуществляется по значению, в стек помещается копия объекта SimpleCat как
локального для вызываемой функции. И это приводит к вызову конструктора копий.
смотри строку 4.
4. Simple Cat Copy Constructor...
5. Далее эта функция производит свою работу и выдает свое сообщение.
6. Затем эта функция возвращает управление программой вызывающей функции
main() и объект класса SimpleCat вновь возвращается как значение. При этом
создается еще одна копия объекта за счет вызова конструктора копий смотри
строку 6.
5. Function One. Returning...
6. Simple Cat Copy Constructor...
7. Значение, возвращаемое из функции FunctionOne() не присваивается ни одному
объекту, поэтому ресурсы, затрачиваемые на создание временного объекта при
реализиции механизма возврата, просто выброшены на ветер как и ресурсы,
затраченные на его удаление с помощью деструктора, который заявил о себе в
строке 7.
То есть этот деструктор уничтожил конструктор копий.
7. Simple Cat Destructor...
8. Потом еще раз вызывается деструктор чтобы уничтожить второй конструктор
копий.
(Конструктор копий был вызван дважды).
8. Simple Cat Destructor...
9. Затем главная программа сообщает о вызове функции FunctionTwo(), на этот
раз параметр передается как ссылка. При этом ни какой копии объекта не
создается, поэтому отсутствует и сообщение от конструктора копий.
10. Затем функция производит свою работу смотри сообщение в строке 10.
Затем возвращается объект класса SimpleCat (снова как ссылка), поэтому нет
обращений к конструктору и деструктору.
9. Calling FunctionTwo...
10. Function Two. Returning...
11. Наконец программа завершается и объект Frisky выходит из области действия,
создавая последнее обращение к деструктору, выводящему свое сообщение.
11. Simple Cat Destructor...
При вызове же второй функции подобных обращений не делается!.
В качестве эксперимента сделал изменения в программе. Заменил указатель
во второй функции на ссылку и получил следующие результаты:
Вот измененная вторая функция:
SimpleCat FunctionTwo (SimpleCat &theCat)
{
Вот обращение к ней из главной функции
return theCat;
cout << "Calling FunctionTwo...\n";
FunctionTwo(Frisky);
1. Making a cat...
2. Simple Cat Constructor...
//вызов первой функции
3. Calling FunctionOne...
4. Simple Cat Copy Constructor...
5. Function One. Returning...
6. Simple Cat Copy Constructor...
7. Simple Cat Destructor...
8. Simple Cat Destructor...
//вызов второй функции
9. Calling FunctionTwo...
10. Function Two. Returning...
11. Simple Cat Copy Constructor...
12. Simple Cat Destructor...
13. Simple Cat Destructor...
Вот какие вызовы были при указателе в параметре функции:
//вызов второй функции
9. Calling FunctionTwo...
10. Function Two. Returning...
Значит ссылки при помощи указателей еще более эффективны, чем при
помощи ссылок или я не прав?(Возможно ты прав, Аристотель)
Содержание