перегрузка оператора =
От: __INFINITE Россия  
Дата: 10.09.06 22:47
Оценка:
Когда я выполняю операцию умножения, то мне надо вернуть что-то:
template<typename T>
Matrix<T>& Matrix<T>::operator *( Matrix & input )
{
    assert( this->width == input.height );
    Matrix<T> * newmatrix = new Matrix<T>(this->width, input.height); // ну, вот например создаю новый объект
    for( unsigned i = 0; i < this->height; i++ )
        for( unsigned j = 0; j < input.width; j++ )
        {
            newmatrix->data[i][j] = 0;
            for( int k = 0; k < this->width; k++ )
                newmatrix->data[i][j] += this->data[k][i] * input.data[j][k];
        }

    return * newmatrix;
}

А потом кто-то это что-то должен присвоить. Но... если он до этого был уже создан, то я просто обновляю его data.
template<typename T>
Matrix<T>& Matrix<T>::operator =( Matrix & input )
{
    assert( this->height == input.height );
    assert( this->width == input.width );
    for( unsigned i = 0; i < this->height; i++ )
        for( unsigned j = 0; j < this->width; j++ )
            this->data[i][j] = input.data[i][j];
    return * this;
}

А сам объект который вернулся в результате умножения куда делся? Его же надо убивать? Я подумывал написать delete прям в operator=, но тогда если я просто делаю X=Y — фигня получается.

Вопрос такой: как надо сделать операцию умножения и присвоения, чтобы память не текла.
Re: перегрузка оператора =
От: gear nuke  
Дата: 10.09.06 23:25
Оценка:
Здравствуйте, __INFINITE, Вы писали:

__I>Когда я выполняю операцию умножения, то мне надо вернуть что-то:

__I>
__I>template<typename T>
__I>Matrix<T>& Matrix<T>::operator *( Matrix & input )
__I>{
__I>    assert( this->width == input.height );
__I>    Matrix<T> * newmatrix = new Matrix<T>(this->width, input.height); // ну, вот например создаю новый объект
__I>    for( unsigned i = 0; i < this->height; i++ )
__I>        for( unsigned j = 0; j < input.width; j++ )
__I>        {
__I>            newmatrix->data[i][j] = 0;
__I>            for( int k = 0; k < this->width; k++ )
__I>                newmatrix->data[i][j] += this->data[k][i] * input.data[j][k];
__I>        }

__I>    return * newmatrix;
__I>}

В данном случае operator*() — это функция-член, поэтому она должна (точнее, обычно так делают) производить действия над обектом *this (и возвращать его же). Если нужен новый объект, то используют обычную функцию (не-член)

template<typename T>
Matrix<T>& operator *(const Matrix & l, const Matrix & r)
{
  Matrix<T> result( l );
  return result._do_multiply(l);
}

__I>А потом кто-то это что-то должен присвоить. Но... если он до этого был уже создан, то я просто обновляю его data.
__I>
__I>template<typename T>
__I>Matrix<T>& Matrix<T>::operator =( const Matrix & input ) // лишнее копирование ни к чему
__I>{
      if ( this != &input ) // и здесь то же
      {
__I>    assert( this->height == input.height );
__I>    assert( this->width == input.width );
__I>    for( unsigned i = 0; i < this->height; i++ )
__I>        for( unsigned j = 0; j < this->width; j++ )
__I>            this->data[i][j] = input.data[i][j];
      }
__I>    return * this;
__I>}

__I>А сам объект который вернулся в результате умножения куда делся? Его же надо убивать? Я подумывал написать delete прям в operator=, но тогда если я просто делаю X=Y — фигня получается.

__I>Вопрос такой: как надо сделать операцию умножения и присвоения, чтобы память не текла.


Аллокацию по new использовать не нужно.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[2]: перегрузка оператора =
От: __INFINITE Россия  
Дата: 11.09.06 08:22
Оценка:
Я правильно понимаю: выполняя X*Y я на стеке в operator* создаю временный объект (с помощью конструктора копирования от X, а потом прото умножаю на Y) — и, если я сделаю Z=X*Y — то я опятьже копированием инициализирую Z — а вот то, что было на стэке оно умрёт.

Проверить сейчас не могу, вечером изучу по-подробнее. А, вообще, спасибо.
Re[2]: перегрузка оператора =
От: Roman Odaisky Украина  
Дата: 11.09.06 09:02
Оценка: 1 (1)
Здравствуйте, gear nuke, Вы писали:

GN>В данном случае operator*() — это функция-член, поэтому она должна (точнее, обычно так делают) производить действия над обектом *this (и возвращать его же). Если нужен новый объект, то используют обычную функцию (не-член)


GN>
GN>template<typename T>
GN>Matrix<T>& operator *(const Matrix & l, const Matrix & r)
GN>{
GN>  Matrix<T> result( l );
GN>  return result._do_multiply(l);
GN>}
GN>

ссылка зачем?!

Проще всего делать так:
class X
{
    X& operator *=(Whatever const& w)
    {
        . . .
        return *this;
    }

    X operator *(Whatever const& w) const
    {
         X tmp(*this);
         return tmp *= w;
    }
};

Но это имеет недостаток: не допускаются неявные преобразования левой части. Поэтому лучше всего так:

class X
{
    friend X& operator *=(X& x, Whatever const& w)
    {
        . . .
        return x;
    }
};

X operator *(X x, Whatever const& w)
{
    return x *= w;
}
До последнего не верил в пирамиду Лебедева.
Re: перегрузка оператора =
От: Кодт Россия  
Дата: 11.09.06 13:18
Оценка: 1 (1)
Здравствуйте, __INFINITE, Вы писали:

Смотри мои комментарии

__I>Когда я выполняю операцию умножения, то мне надо вернуть что-то:

__I>template<typename T>
__I>Matrix<T> // возвращать надо новый временный объект, а не ссылку на созданный на куче и в дальнейшем потерянный
__I>Matrix<T>::operator *( Matrix const& input ) const // ты же не собираешься модифицировать аргументы, правильно?
__I>{
__I>    assert( this->width == input.height );
__I>    Matrix<T> newmatrix(this->width, input.height); // создаёшь локальный объект на стеке...
__I>    for( unsigned i = 0; i < this->height; i++ )
__I>        for( unsigned j = 0; j < input.width; j++ )
__I>        {
__I>            newmatrix.data[i][j] = 0;
__I>            for( int k = 0; k < this->width; k++ )
__I>                newmatrix.data[i][j] += this->data[k][i] * input.data[j][k];
__I>        }

__I>    return newmatrix; // и возвращаешь его копию (вообще-то, компилятор выполнит NRVO и сэкономит на копировании)
__I>}

__I>А потом кто-то это что-то должен присвоить. Но... если он до этого был уже создан, то я просто обновляю его data.
__I>template<typename T>
__I>Matrix<T>& Matrix<T>::operator =( Matrix const& input ) // источник не меняется - объяви его константным
__I>{
__I>    assert( this->height == input.height );
__I>    assert( this->width == input.width );
__I>    for( unsigned i = 0; i < this->height; i++ )
__I>        for( unsigned j = 0; j < this->width; j++ )
__I>            this->data[i][j] = input.data[i][j];
__I>    return * this;
__I>}
__I>


__I>А сам объект который вернулся в результате умножения куда делся? Его же надо убивать? Я подумывал написать delete прям в operator=, но тогда если я просто делаю X=Y — фигня получается.

__I>Вопрос такой: как надо сделать операцию умножения и присвоения, чтобы память не текла.

Не надо создавать объекты на куче — тогда и убивать ничего не потребуется.

Но, конечно, внутренние данные объектов — должны быть на куче.
Кстати, подозреваю, что data у тебя — это jagged array (массив с рваным краем), т.е.
T** data;
........
data = new T* [height];
for( unsigned i = 0; i < height; ++i ) data[i] = new T [width];

Это довольно расточительно и по памяти, и по скорости доступа. Лучше использовать линейный массив и функцию пересчёта (i,j) -> index.
template<class T> class Matrix
{
    T* data;
    unsigned width,height;
    unsigned reserved; // текущий размер массива: width*height <= reserved
    
    Matrix() : data(NULL), width(0), height(0), reserved(0) {}
.....
    void resize(unsigned w, unsigned h)
    {
        unsigned s = w*h; // потребный размер
        if(s > reserved)
        {
            delete[]data;
            data = new T[s];
            reserved = s;
        }
        width=w; height=h;
    }
.....
    unsigned indexof(unsigned i, unsigned j) const { assert(i<width && j<height); return i*width + j; }
.....    
    T      & at(unsigned i, unsigned j)       { return data[indexof(i,j)]; }
    T const& at(unsigned i, unsigned j) const { return data[indexof(i,j)]; }
.....
};

и в алгоритмах...
newmatrix.at(i,j) += this->at(i,k) * input.at(k,j);

Кстати, тогда оператор умножения можно сделать внешней функцией (заодно — поддержишь умножение на скаляр слева, c*M; оператор-член позволяет только умножать справа: M*c)

А присваивание сведётся к поэлементному копированию массива
if(this != &input)
{
    resize(input.width,input.height);
    std::copy(input.data, input.data+input.width*input.height, data);
}
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[2]: перегрузка оператора =
От: __INFINITE Россия  
Дата: 11.09.06 15:12
Оценка:
Здравствуйте, Кодт.
Огромное вам спасибо за подробное объяснение!
Re[3]: перегрузка оператора =
От: gear nuke  
Дата: 11.09.06 21:40
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

GN>>
GN>>template<typename T>
GN>>Matrix<T>& operator *(const Matrix & l, const Matrix & r)
GN>>{
GN>>  Matrix<T> result( l );
GN>>  return result._do_multiply(l);
GN>>}

RO>ссылка зачем?!

Проблемы copy-paste
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[3]: перегрузка оператора =
От: gear nuke  
Дата: 11.09.06 21:50
Оценка:
Здравствуйте, __INFINITE, Вы писали:

__I>Я правильно понимаю: выполняя X*Y я на стеке в operator* создаю временный объект (с помощью конструктора копирования от X, а потом прото умножаю на Y) — и, если я сделаю Z=X*Y — то я опятьже копированием инициализирую Z —


В некоторых случаях копирование (и при возврате значения) будет исключено (заоптимизировано) компилятором. Это более вероятно для классов без user-defined конструктора копирования и оператора присваивания. Если есть возможность использовать матрицы фиксированного размера, я бы посмотрел на std::tr1::array (boost::array) и доработал напильником по совету Кодта
Автор: Кодт
Дата: 11.09.06


__I>а вот то, что было на стэке оно умрёт.


Да, объекты на стеке потому и называются автоматическими
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.