Matrix m2 // тип правого операнда
)
{
Matrix result;
// необходимые действия
return result;
}
При такой реализации сложение двух объектов типа Matrix выглядит вполне привычно:
a + b;
но, к сожалению, оказывается совершенно неэффективным. Заметим, что параметры у нас передаются по значению. Содержимое двух матриц будет копироваться в область активации функции operator+(), а поскольку объекты типа Matrix весьма велики, затраты времени и памяти на создание копий могут быть совершенно неприемлемыми.
Представим себе, что мы решили использовать указатели в качестве параметров, чтобы избежать этих затрат. Вот модифицированный код operator+():
// реализация с параметрами-указателями
operator+( Matrix *ml, Matrix *m2 )
{
Matrix result;
// необходимые действия
return result;
}
Да, мы добились эффективной реализации, но зато теперь применение нашей операции вряд ли можно назвать интуитивно понятным. В качестве значений параметров-указателей требуется передавать адреса складываемых объектов. Поэтому для сложения двух матриц пришлось бы написать:
a + b; // допустимо, хотя и плохо
Хотя такая форма не может не вызвать критику, но все-таки два объекта сложить еще удается. А вот три уже крайне затруднительно:
// а вот это не работает
// a + b возвращает объект типа Matrix
a + b + c;
Для того чтобы сложить три объекта, при подобной реализации нужно написать так:
// правильно: работает, однако ...
( a + b ) + c;
Трудно ожидать, что кто-нибудь согласится писать такие выражения. К счастью, параметры-ссылки дают именно то решение, которое требуется. Если параметр объявлен как ссылка, функция получает его l-значение, а не копию. Лишнее копирование исключается. И тип фактического аргумента может быть Matrix – это упрощает операцию сложения, как и для встроенных типов. Вот схема перегруженного оператора сложения для класса Matrix:
// реализация с параметрами-ссылками
operator+( const Matrix m1, const Matrix m2 )
{
Matrix result;
// необходимые действия
return result;
}
При такой реализации сложение трех объектов Matrix выглядит вполне привычно:
a + b + c;
Ссылки были введены в С++ именно для того, чтобы удовлетворить двум требованиям: эффективная реализация и интуитивно понятное применение.
7.3.3. Параметры-массивы
Массив в С++ никогда не передается по значению, а только как указатель на его первый, точнее нулевой, элемент. Например, объявление
void putValues( int[ 10 ] );
рассматривается компилятором так, как будто оно имеет вид
void putValues( int* );
Размер массива неважен при объявлении параметра. Все три приведенные записи эквивалентны:
// три эквивалентных объявления putValues()
void putValues( int* );
void putValues( int[] );
void putValues( int[ 10 ] );
Передача массивов как указателей имеет следующие особенности:
• изменение значения аргумента внутри функции затрагивает сам переданный объект, а не его локальную копию. Если такое поведение нежелательно, программист должен позаботиться о сохранении исходного значения. Можно также при объявлении функции указать, что она не должна изменять значение параметра, объявив этот параметр константой:
void putValues( const int[ 10 ] );
*
размер массива не является частью типа параметра. Поэтому функция не знает реального размера передаваемого массива. Компилятор тоже не может это проверить. Рассмотрим пример:
void putValues( int[ 10 ] ); // рассматривается как int*
int main() {
int i, j [ 2 ];
putValues( i ); // правильно: i is int*;
// однако при выполнении возможна ошибка
putValues( j ); // правильно: j - адрес 0-го элемента - int*;
// однако при выполнении возможна ошибка
*
При проверке типов параметров компилятор способен распознать, что в обоих случаях тип аргумента int* соответствует объявлению функции. Однако контроль за тем, не является ли аргумент массивом, не производится.