Próbuję zbudować klasę szablon {X0}} za pomocą konstruktora, który akceptuje 4 parametry jako 4 podłoże a, {x2}}, c, d zajmująca Cztery ćwiartki (a = Northwest, b = Northeast, c = Southwest, d = południowo-wschodnia) macierzy. Pokazane poniżej:

    template<class T> class SquareMatrix {
    public:
        SquareMatrix(){}
        SquareMatrix(const T first, const T second, const T third, const T fourth) {
            a = first;
            b = second;
            c = third;
            d = fourth;
        }
        SquareMatrix<T>(const SquareMatrix<T>& rhs) { // copy constructor
            a = rhs.getA();
            b = rhs.getB();
            c = rhs.getC();
            d = rhs.getD();
        }
        SquareMatrix& operator=(const SquareMatrix rhs) {  // assignment operator
            if (&rhs != this) {
                SquareMatrix(rhs);
            }

            return *this;
        }
        ~SquareMatrix() {}  // destructor

        // getters and setters
        T getA() const {return a;}
        T getB() const {return b;}
        T getC() const {return c;}
        T getD() const {return d;}
        void setA(const T& input) {a = input;}
        void setB(const T& input) {b = input;}
        void setC(const T& input) {c = input;}
        void setD(const T& input) {d = input;}

    private:   
        // 4 quadrants
        // [a, b;
        //  c, d]
        T a, b, c, d;
    };

    template<class T> SquareMatrix<T> operator+(const SquareMatrix<T> lhs, 
                      const SquareMatrix<T>& rhs) {
        SquareMatrix<T> ret(lhs);
        ret.setA( ret.getA() + rhs.getA() );
        ret.setB( ret.getB() + rhs.getB() );
        ret.setC( ret.getC() + rhs.getC() );
        ret.setD( ret.getD() + rhs.getD() );

        return ret;
    };
    template<class T> SquareMatrix<T> operator-(const SquareMatrix<T> lhs,
                      const SquareMatrix<T>& rhs) {
        SquareMatrix<T> ret(lhs);
        ret.setA( ret.getA() - rhs.getA() );
        ret.setB( ret.getB() - rhs.getB() );
        ret.setC( ret.getC() - rhs.getC() );
        ret.setD( ret.getD() - rhs.getD() );

        return ret;
    };
    // this is the implementation of Strassen's algorithm
    template<class T> SquareMatrix<T> operator*(const SquareMatrix<T>& lhs, 
                      const SquareMatrix<T>& rhs) {
        T product_1 = lhs.getA() * ( rhs.getB() - rhs.getD() );
        T product_2 = ( lhs.getA() + lhs.getB() ) * rhs.getD();
        T product_3 = ( lhs.getC() + lhs.getD() ) * rhs.getA();
        T product_4 = lhs.getD() * ( rhs.getC() - rhs.getA() );
        T product_5 = ( lhs.getA() + lhs.getD() ) * ( rhs.getA() + rhs.getD() );
        T product_6 = ( lhs.getB() - lhs.getD() ) * ( rhs.getC() + rhs.getD() );
        T product_7 = ( lhs.getA() - lhs.getC() ) * ( rhs.getA() + rhs.getB() );
        SquareMatrix<T> ret;
        ret.setA(product_5 + product_4 - product_2 + product_6);
        ret.setB(product_1 + product_2);
        ret.setC(product_3 + product_4);
        ret.setD(product_1 + product_5 - product_3 - product_7);

        return ret;
    };

Teraz próbuję utworzyć zagnieżdżoną matrycę 4x4, robiąc:

    int main() {
        cout << "Example: a 4x4 matrix: " << endl;
        // 4 single quadrants 
        SquareMatrix<int> M_1A(1, 2, 3, 4);
        SquareMatrix<int> M_1B(5, 6, 7, 8);
        SquareMatrix<int> M_1C(9, 10, 11, 12);
        SquareMatrix<int> M_1D(13, 14, 15, 16);
        // 4x4 matrix M_1
        SquareMatrix< SquareMatrix<int> > M_1(M_1A, M_1B, M_1C, M_1D);
        // test
        cout << "test: " << endl;
        cout << M_1.getA().getA() << endl;

        return 0;
    }

Zamierzona wyjście macierzy powinno być M_1 = [1,2,5,6; 3,4,7,8; 9,10,13,14; 11,12,15,16]. Używam polecenia {{X1} (Ostatni raz próbowałem, dał 6684672). Czy istnieje sposób na wdrożenie klasy matrycy w ten sposób?

(Edytuj: Obecnie zawarte operator przydzielania i destruktor, prawdopodobne źródła błędu)

0
Giogre 14 marzec 2020, 23:36

2 odpowiedzi

Najlepsza odpowiedź

Wraz ze sugerowanymi komentarzami jest operator przydziałowy, który jest uszkodzony.

SquareMatrix& operator=(const SquareMatrix rhs) {
    if (&rhs != this) {
         SquareMatrix(rhs); // <-- This creates a temporary that 
                            // dies off after that line is executed
    }
    return *this;
}

Operator przydziału nie wykonuje żadnych przydziału. Zamiast tego dokonano tymczasowego SquareMatrix.

Naprawić ten problem

1) Nie dostarczaj żadnych operatora przypisywania, konstruktora kopiowania lub destruktora, ponieważ typ T powinien być bezpiecznie kopiowania.

2) Napraw operator przypisania, aby poprawnie działa:

#include <algorithm>
//...
SquareMatrix& operator=(const SquareMatrix rhs) {
    if (&rhs != this) {
         SquareMatrix t(rhs); 
         std::swap(t.a, a); 
         std::swap(t.b, b); 
         std::swap(t.c, c); 
         std::swap(t.d, d); 
    }
    return *this;
}

Przypisanie działa teraz. Jednak sugeruję, że nie pisze kod, który nie musi być napisany, a twoja implementacja buggy jest w punkcie.

W twoim przypadku, jeśli pozwolisz kompilatorowi wygenerować operatora przypisania i / lub polegać na T w szablonie, aby mieć poprawne semantyki kopii, Twoja klasa będzie działała poprawnie.

1
PaulMcKenzie 14 marzec 2020, 22:07

Komentarze Pawła są tuż przy znaku. Chociaż twój Squarexix nie jest wbudowany, jest zadeklarowany składa się z 4 elementów typu T. Domyślna kopia C'tor dla Twojej klasy będzie używana operatora przydziału lub przypisania C'tor o rzeczywistym rodzaju, który jest reprezentowany W twoim użyciu.

Mam pewne sugestie, aby ulepszyć kod:

  1. Jeśli T ma typ, który ma ślad pamięci, który jest większy niż wskaźnik / Int: bardziej wydajny jest pozwolenie, aby Twój C'tor otrzymał elementy Bij Const Reference taka:
    SquareMatrix( const T& _a, const T& _b, const T& _c, const T& _d)
  1. Użyj konstruktorów kopiowania jak najwięcej: w ten sposób cztery elementy nie są pierwsze zainicjowane, a następnie przypisane później. Zamiast tego są inicjalizowane z odpowiednimi wartościami na raz.
     SquareMatrix( const T& _a, const T& _b, const T& _c, const T& _d)
     : a( _a), b( _b), c( _c), d( _d)
     { /* empty body */ }
  1. Wybierz swoje nazwy mądrze, aby uprościć rzeczy. Nie wprowadzaj dodatkowych mapów w schematach nazw, kiedy nie musisz; Właśnie stwarza możliwości poślizgu. Zastosowałem już, że w pkt 1 powyżej :-).
  2. Nie "Programuj przez życzeniowe myślenie": Pisanie w komentarzach lub nazwach typu / zmienne, które coś ma być coś, czego nie będzie tego zrobić. W twoim przypadku: Twoja klasa nie jest matrycą kwadratową, ani nawet matrycą. Do kompilatora jest typem danych składający się z czterech elementów, odpowiednio o nazwie A, B, C i D, o typ T, który należy zdefiniować w czasie kompilacji.
1
Andreas_75 14 marzec 2020, 22:42