Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

operator=() przenoszący c++11

Ostatnio zmodyfikowano 2016-05-17 00:47
Autor Wiadomość
mateczek
Temat założony przez niniejszego użytkownika
operator=() przenoszący c++11
» 2016-05-16 15:05:39
Chciałem sprawdzić na przykładzie coś co wyczytałem odnośnie referencji && na R-Value, konstruktorów przenoszących, i przenoszącego operatora przypisania.
Napisałem klasę do mnożenia macierzy.
PS. macierz 2D trzymam w tablicy jednowymiarowej by łatwiej operować pamięcią


C/C++
#include <iostream>

using namespace std;
class matrix {
    int * tab = NULL;
    int wiersze = 0, kolumny = 0;
public:
    friend ostream & operator <<( ostream & s, matrix & m );
   
    matrix( int w, int k )
        : wiersze( w )
         , kolumny( k )
    { //konstruktor
        cout << "jestem konstruktor" << endl;
        int rozmiar = w * k;
        tab = new int[ rozmiar ];
    }
   
    matrix() { } //konstruktor domyślny nic nie robi
    ~matrix() {
        if( tab ) {
            delete[] tab;
            tab = NULL;
        }
    }
   
   
    int & operator ()( int w, int k ) {
        return tab[( w - 1 ) * kolumny + k - 1 ];
    }
   
    int row() { return wiersze; }
    int column() { return kolumny; }
    matrix & operator =( matrix && wzor ); //operator przenoszenia
    matrix( const matrix & wzor ); //konstruktor kopiujący    
    matrix operator *( matrix & m ); //operator mnożenia macierzy
    matrix & operator =( const matrix & wzor ); //operator przypisania. Ten nie powinien się odpalić w c++11
};



matrix & matrix::operator =( matrix && wzor )
{
    //operator przenoszenia
    cout << "jestem operator przenoszenia" << endl;
    tab = wzor.tab; //przepisanie tylko wskaźnika na macierz bez kopiowania danych
    wiersze = wzor.wiersze; //przepisanie zmiennych "wiersz" oraz "kolumna"
    kolumny = wzor.kolumny;
    //niszczenie obiektu wzór
    wzor.tab = NULL; //wyzerowanie wskaźnika obiektu wzór
    wzor.wiersze = 0; //wyzerowanie zmiennych w obiekcie wzór
    wzor.kolumny = 0;
    return * this;
}

matrix::matrix( const matrix & wzor ) {
    cout << "jestem konstruktor kopiujący" << endl;
    wiersze = wzor.wiersze;
    kolumny = wzor.kolumny;
    int rozmiar = wiersze * kolumny;
    tab = new int[ rozmiar ];
    for( int i = 0; i < rozmiar; i++ ) { //kopiowanie bufora
        tab[ i ] = wzor.tab[ i ];
    }
}

matrix & matrix::operator =( const matrix & wzor ) {
    cout << "jestem operator przypisania" << endl;
    wiersze = wzor.wiersze;
    kolumny = wzor.kolumny;
    int rozmiar = wiersze * kolumny;
    if( tab ) delete[] tab; // zwalnianie pamięci ze starej tablicy
   
    tab = new int[ rozmiar ]; // alokacja nowej
    for( int i = 0; i < rozmiar; i++ ) { //kopiowanie bufora
        tab[ i ] = wzor.tab[ i ];
    }
}

matrix matrix::operator *( matrix & m ) {
    if( kolumny == m.wiersze ) { //tylko wtedy mnożenie ma sens
        matrix temp( wiersze, m.kolumny ); //obiekt tymczasowy tworzenie
        // dalej mnożenie czyli wypełnianie wyżej stworzonego obiektu tymczasowego
        for( int w = 1; w <= temp.wiersze; w++ ) {
            for( int k = 1; k <= temp.kolumny; k++ ) {
                int element = 0;
                for( int i = 1; i <= kolumny; i++ ) element += operator ()( w, i ) * m( i, k );
               
                temp( w, k ) = element;
            }
        }
        //koniec mnożenia zwracam obiekt tymczasowy
        return temp;
    } // ponieważ tylko macierze, w których liczba kolumn macierzy "A" jest równa liczbie wierszy macierzy "B" da się pomnożyć
    cout << "nie da sie pomnorzyc" << endl;
    //funkcja nic nie zwraca w przypadku błędnych danych wejściowych
}

ostream & operator <<( ostream & s, matrix & m ) {
    for( int w = 1; w <= m.wiersze; w++ ) {
        for( int k = 1; k <= m.kolumny; k++ ) {
            s << m( w, k ) << " ";
        }
        s << endl;
    }
    return s;
}

int main() {
    matrix m1( 5, 5 ); //macierz 5 na 5
    matrix m2( 5, 4 ); //macierz 5 na 4
   
    //wypełniam jedynkami
    for( int w = 1; w <= m1.row(); w++ ) {
        for( int k = 1; k <= m1.column(); k++ ) {
            m1( w, k ) = 1;
        }
    }
    cout << m1 << endl;
   
    //wypełniam dwójkami
    for( int w = 1; w <= m2.row(); w++ ) {
        for( int k = 1; k <= m2.column(); k++ ) {
            m2( w, k ) = 2;
        }
    }
    cout << m2 << endl;
   
    matrix m3;
    m3 = m1 * m2;
    cout << m3 << endl;
}
wynik działania programu
https://zapodaj.net/images​/e6f7041f2504a.png

jestem konstruktor
jestem konstruktor
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1

2 2 2 2
2 2 2 2
2 2 2 2
2 2 2 2
2 2 2 2

jestem konstruktor              //w funkcji operator*() tworzony jest obiekt tymczasowy                                          OK
jestem konstruktor kopiujący    //ten konstruktor nie powinien się pojawić bo chciałem tylko przenieść tablicę a nie ją kopiować NOK
jestem operator przenoszenia    //ostateczne przypisanie                                                                         OK  
10 10 10 10
10 10 10 10
10 10 10 10
10 10 10 10
10 10 10 10
Zainteresowała mnie możliwość przenoszenia samych wskaźników na bufor zadeklarowany dynamicznie.  Z tąd właśnie moje zapytanie do kodu

C/C++
matrix m1( 5, 5 ); //tu działa konstruktor pierwszego obiektu
matrix m2( 5, 4 ); //tu działa konstruktor obiektu dwa

matrix m3; // tu powinien zadziałać konstruktor domyślny

m3 = m1 * m2; //operator mnożenia tworzy obiekt tymczasowy i wypełnia go prawidłowymi wynikami - konstruktor obiektu tymczasowego
//na koniec do obiektu m3 powinien być wywołany operator przenoszenia.     
Jednak gdzieś między konstrukcją obiektu tymczasowego w funkcji operatror*() a przenoszącym operatorem przypisania, Uruchamia się konstruktor kopiujący.
Ten konstruktor kopiujący sprawia, że cały zysk możliwości przeniesienia tylko samego wskaźnika na dynamiczną tablicę idzie w diabły.

Moje pytanie jest następujące co źle robię ?? i gdzie można lepiej

PS. Programik z macierzami napisałem dla empirycznego sprawdzenia nowej dla mnie metody przenoszenia dynamicznie alokowanych tablic
P-148299
Monika90
» 2016-05-16 17:02:36
Nie masz konstruktora przenoszącego matrix(matrix&& wzor), więc kompilator używa kopiującego.

C/C++
cout << "nie da sie pomnorzyc" << endl;
//funkcja nic nie zwraca w przypadku błędnych danych wejściowych
Tak nie można, jeżeli nie da się zwrócić poprawnej wartości to rzuć wyjątkiem.
P-148303
mateczek
Temat założony przez niniejszego użytkownika
» 2016-05-16 17:52:31
nie masz konstruktora przenoszącego matrix(matrix&& wzor)
 Dzięki wielkie za wskazówkę zaraz przetestuje. Tylko jeszcze doczytam na temat std::move, którego zaleca się używać w konstruktorach przenoszących.
P-148307
j23
» 2016-05-16 18:21:54
C/C++
matrix & matrix::operator =( matrix && wzor )
{
    tab = wzor.tab;
    ...
}
A co jeśli this.tab nie jest null?
P-148309
mateczek
Temat założony przez niniejszego użytkownika
» 2016-05-17 00:47:32
Dzięki jeszcze raz Monika
Dopisanie konstruktora przenoszącego faktycznie spowodowało, że zamiast konstruktora kopiującego odpalił się przenoszący. A dodanie wyjątku jeszcze lepiej zadziałało. Bo żaden z nich nie był potrzebny.
C/C++
matrix matrix::operator *( matrix & m ) {
    if( kolumny != m.wiersze ) throw "nie pomnozysz takich macierzy";
   
    matrix temp( wiersze, m.kolumny ); //obiekt tymczasowy tworzenie
    // dalej mnożenie czyli wypełnianie wyżej stworzonego obiektu tymczasowego
    for( int w = 1; w <= temp.wiersze; w++ ) {
        for( int k = 1; k <= temp.kolumny; k++ ) {
            int element = 0;
            for( int i = 1; i <= kolumny; i++ ) element += operator ()( w, i ) * m( i, k );
           
            temp( w, k ) = element;
        }
    }
    //koniec mnożenia zwracam obiekt tymczasowy
    return temp;
}
int main() {
    //[...]
    matrix m3;
    try {
        m3 = m1 * m2;
        cout << m3 << endl;
    }
    catch( char const * p ) {
        cout << p << endl;
    }
}

wynik zadowalający:


jestem konstruktor
jestem konstruktor
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1

2 2 2 2
2 2 2 2
2 2 2 2
2 2 2 2
2 2 2 2

jestem konstruktor            // tutaj konstruktor obiektu tymczasowego
jestem operator przenoszenia  // i operator przeniesienia
10 10 10 10
10 10 10 10
10 10 10 10
10 10 10 10
10 10 10 10


@j23

C/C++
matrix & matrix::operator =( matrix && wzor )
{
    tab = wzor.tab;
    ...
}
A co jeśli this.tab nie jest null?
}
Słuszna uwaga. Będzie wyciek pamięci;) Przenosząc powinienem skorzystać z std::swap (zamiana).
Wówczas z obiektem tymczasowym uporają się mechanizmy niszczenia obiektów tymczasowych.
P-148318
« 1 »
  Strona 1 z 1