mat_add( Matrix &result,
const Matrix& m1, const Matrix& m3 )
{
// вычислить результат
}
Таким образом, проблема производительности решается, но для класса уже нельзя использовать операторный синтаксис, так что теряется возможность инициализировать объекты
// более не поддерживается
Matrix c = a + b;
и использовать их в выражениях:
// тоже не поддерживается
if ( a + b c ) ...
Неэффективный возврат объекта класса - слабое место С++. В качестве одного из решений предлагалось расширить язык, введя имя возвращаемого функцией объекта:
Matrix&
operator+( const Matrix& m1, const Matrix& m2 )
name result
{
Matrix result;
// ...
return result;
}
Тогда компилятор мог бы самостоятельно переписать функцию, добавив к ней третий параметр-ссылку:
// переписанная компилятором функция
// в случае принятия предлагавшегося расширения языка
void
operator+( Matrix &result, const Matrix& m1, const Matrix& m2 )
name result
{
// вычислить результат
}
и преобразовать все вызовы этой функции, разместив результат непосредственно в области, на которую ссылается первый параметр. Например:
Matrix c = a + b;
было бы трансформировано в
Matrix c;
operator+(c, a, b);
Это расширение так и не стало частью языка, но предложенная оптимизация прижилась. Компилятор в состоянии распознать, что возвращается объект класса и выполнить трансформацию его значения и без явного расширения языка. Если дана функция общего вида:
classType
functionName( paramList )
{
classType namedResult;
// выполнить какие-то действия ...
return namedResult;
}
то компилятор самостоятельно трансформирует как саму функцию, так и все обращения к ней:
void
functionName( classType &namedResult, paramList )
{
// вычислить результат и разместить его по адресу namedResult
}
что позволяет уйти от необходимости возвращать значение объекта и вызывать копирующий конструктор. Чтобы такая оптимизация была применена, в каждой точке возврата из функции должен возвращаться один и тот же именованный объект класса.
И последнее замечание об эффективности работы с объектами в C++. Инициализация объекта класса вида
Matrix c = a + b;
всегда эффективнее присваивания. Например, результат следующих двух инструкций такой же, как и в предыдущем случае:
Matrix c;
c = a + b;
но объем требуемых вычислений значительно больше. Аналогично эффективнее писать:
for ( int ix = 0; ix
чем
Matrix matSum;
for ( int ix = 0; ix
Причина, по которой присваивание всегда менее эффективно, состоит в том, что возвращенный локальный объект нельзя подставить вместо объекта в левой части оператора присваивания. Иными словами, в то время как инструкцию
Point3d p3 = operator+( p1, p2 );
можно безопасно трансформировать:
// Псевдокод на C++
Point3d p3;
operator+( p3, p1, p2 );
преобразование
Point3d p3;
p3 = operator+( p1, p2 );
в
// Псевдокод на C++
// небезопасно в случае присваивания
operator+( p3, p1, p2 );
небезопасно.
Преобразованная функция требует, чтобы переданный ей объект представлял собой неформатированную область памяти. Почему? Потому что к объекту сразу применяется конструктор, который уже был применен к именованному локальному объекту. Если переданный объект уже был сконструирован, то делать это еще раз с семантической точки зрения неверно.
Что касается инициализируемого объекта, то отведенная под него память еще не подвергалась обработке. Если же объекту присваивается значение и в классе объявлены конструкторы (а именно этот случай мы и рассматриваем), можно утверждать, что эта память уже форматировалась одним из них, так что непосредственно передавать объект функции небезопасно.