А сам объект который вернулся в результате умножения куда делся? Его же надо убивать? Я подумывал написать delete прям в operator=, но тогда если я просто делаю X=Y — фигня получается.
Вопрос такой: как надо сделать операцию умножения и присвоения, чтобы память не текла.
Здравствуйте, __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 (и возвращать его же). Если нужен новый объект, то используют обычную функцию (не-член)
__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
Я правильно понимаю: выполняя X*Y я на стеке в operator* создаю временный объект (с помощью конструктора копирования от X, а потом прото умножаю на Y) — и, если я сделаю Z=X*Y — то я опятьже копированием инициализирую Z — а вот то, что было на стэке оно умрёт.
Проверить сейчас не могу, вечером изучу по-подробнее. А, вообще, спасибо.
Здравствуйте, gear nuke, Вы писали:
GN>В данном случае operator*() — это функция-член, поэтому она должна (точнее, обычно так делают) производить действия над обектом *this (и возвращать его же). Если нужен новый объект, то используют обычную функцию (не-член)
GN>
Смотри мои комментарии
__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>А сам объект который вернулся в результате умножения куда делся? Его же надо убивать? Я подумывал написать 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)]; }
.....
};
Кстати, тогда оператор умножения можно сделать внешней функцией (заодно — поддержишь умножение на скаляр слева, c*M; оператор-член позволяет только умножать справа: M*c)
А присваивание сведётся к поэлементному копированию массива
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
Здравствуйте, __INFINITE, Вы писали:
__I>Я правильно понимаю: выполняя X*Y я на стеке в operator* создаю временный объект (с помощью конструктора копирования от X, а потом прото умножаю на Y) — и, если я сделаю Z=X*Y — то я опятьже копированием инициализирую Z —
В некоторых случаях копирование (и при возврате значения) будет исключено (заоптимизировано) компилятором. Это более вероятно для классов без user-defined конструктора копирования и оператора присваивания. Если есть возможность использовать матрицы фиксированного размера, я бы посмотрел на std::tr1::array (boost::array) и доработал напильником по совету Кодта
Да, объекты на стеке потому и называются автоматическими
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