Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих.pdf
Скачиваний:
183
Добавлен:
01.05.2014
Размер:
3.97 Mб
Скачать

"put function declarations in header files"

"use abstract container types instead of built-in arrays"

"declare class parameters as references"

"use reference to const types for invariant parameters" "use less than eight parameters"

Упражнение 7.8

В каком случае вы применили бы параметр-указатель? А в каком – параметр-ссылку? Опишите достоинства и недостатки каждого способа.

7.4. Возврат значения

В теле функции может встретиться инструкция return. Она завершает выполнение функции. После этого управление возвращается той функции, из которой была вызвана

return;

данная. Инструкция return может употребляться в двух формах: return expression;

Первая форма используется в функциях, для которых типом возвращаемого значения является void. Использовать return в таких случаях обязательно, если нужно принудительно завершить работу. (Такое применение return напоминает инструкцию break, представленную в разделе 5.8.) После конечной инструкции функции подразумевается наличие return. Например:

void d_copy( double "src, double *dst, int sz )

{

/* копируем массив "src" в "dst"

* для простоты предполагаем, что они одного размера */

// завершение, если хотя бы один из указателей равен 0

if ( !src || !dst ) return;

//завершение,

//если указатели адресуют один и тот же массив if ( src == dst )

return;

// копировать нечего if ( sz == 0 )

return;

//все еще не закончили?

//тогда самое время что-то сделать

for ( int ix = 0; ix < sz; ++ix ) dst[ix] = src[ix];

// явного завершения не требуется

}

Во второй форме инструкции return указывается то значение, которое функция должна вернуть. Это значение может быть сколь угодно сложным выражением, даже содержать вызов функции. В реализации функции factorial(), которую мы рассмотрим в следующем разделе, используется return следующего вида:

return val * factorial(val-1);

В функции, не объявленная с void в качестве типа возвращаемого значения, обязательно использовать вторую форму return, иначе произойдет ошибка компиляции. Хотя компилятор не отвечает за правильность результата, он сможет гарантировать его наличие. Следующая программа не компилируется из-за двух мест, где программа завершается без возврата значения:

// определение интерфейса класса Matrix #include "Matrix.h"

bool is_equa1( const Matrix &ml, const Matrix &m2 )

{

/* Если содержимое двух объектов Matrix одинаково,

*возвращаем true;

*в противном случае - false

*/

// сравним количество столбцов

if ( ml.colSize() != m2.co1Size() )

// ошибка: нет возвращаемого значения return;

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

if ( ml.rowSize() != m2.rowSize() )

// ошибка: нет возвращаемого значения return;

//пробежимся по обеим матрицам, пока

//не найдем неравные элементы

for ( int row = 0; row < ml.rowSize(); ++row ) for ( int col = 0; co1 < ml.colSize(); +

+co1 )

if ( ml[row][col] != m2[row][col] ) return false;

//ошибка: нет возвращаемого значения

//для случая равенства

}

Если тип возвращаемого значения не точно соответствует указанному в объявлении функции, то применяется неявное преобразование типов. Если же стандартное приведение невозможно, происходит ошибка компиляции. (Преобразования типов рассматривались в разделе 4.1.4.)

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

Matrix grow( Matrix* p )

{

Matrix val; // ...

return val;

return. Например:

}

grow() возвращает вызывающей функции копию значения, хранящегося в переменной val.

Такое поведение можно изменить, если объявить, что возвращается указатель или ссылка. При возврате ссылки вызывающая функция получает l-значение для val и потому может модифицировать val или взять ее адрес. Вот как можно объявить, что grow() возвращает ссылку:

Matrix& grow( Matrix* p ) {

Matrix *res;

//выделим память для объекта Matrix

//большого размера

//res адресует этот новый объект

//скопируем содержимое *p в *res return *res;

}

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

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

возврат ссылки на локальный объект, время жизни которого ограничено временем выполнения функции. (О времени жизни локальных объектов речь пойдет в разделе 8.3.) По завершении функции такой ссылке соответствует

//ошибка: возврат ссылки на локальный объект

Matrix& add( Matrix &m1, Matrix &m2 )

{

Matrix result:

if ( m1.isZero() ) return m2;

if ( m2.isZero() ) return m1;

//сложим содержимое двух матриц

//ошибка: ссылка на сомнительную область

памяти // после возврата return result;

область памяти, содержащая неопределенное значение. Например:

}

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

Matrix add( ... )

функция возвращает l-значение. Любая его модификация затрагивает сам объект. Например: