• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

C++ Macierz składająca się z elementów typu Wektor - błąd dodawania/wypisywania

VPS Starter Arubacloud
0 głosów
238 wizyt
pytanie zadane 9 maja 2023 w C i C++ przez Robert Lapins Nowicjusz (150 p.)

Cześć wszystkim!
Program ogólnie mi działał i zaliczyłem nim ćwiczenie na zajęciach. (Jakimś cudem zwracał poprawne wartości.)
Aczkolwiek sam już też nie wiem, chat-GPT-4 też się załamał. Najwidoczniej jest jakiś problem z alokacją pamięci, mimo wykorzystania wszędzie wskaźników.

W programie tworzę Macierze, na które składają się elementy typu Wektor, na którego z kolei składają się elementy typu 'double'. Tak więc dodawanie Macierzy to wielokrotne dodawanie Wektorów (a których, to po prostu dodawanie doubli).
Zwracane wartości elementów Wektorów to albo zera albo jakieś losowe liczby wykładnicze.

Będę mega wdzięczny za komentarz weterana programowania, mój wykładowca to chyba ponad 80-letni facet i ciężko mu idzie rozumienie mojego problemu.

Embacandero - Compiler Name: TDM-GCC 9.2.0 64-bit Release zwraca:

(12, 14, 25)    (11, 22, 99)
(10, 20, 30)    (10, 20, 30)

(10, 20, 30)    (11, 22, 99)
(10, 20, 30)    (12, 14, 25)

(1.20896e-316, 0, 0)    (0, 0, 0)
(1.20808e-316, 8.61746e-317, 1.20777e-316)      (-2.95957e-44, 0, 0)

A Visual Studio Code z g++ (Rev4, Built by MSYS2 project) 13.1.0 zwraca nawet mniej:

(12, 14, 25)    (11, 22, 99)
(10, 20, 30)    (0, 0, 0)

Macierz M2:
(0, 0, 0)       (11, 22, 99)
(10, 20, 30)    (12, 14, 25)

Macierz M3 (suma M1 i M2):
(9.85394e-312, 9.85395e-312, 0) (

 

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
#include <iostream>
#include <fstream>
#include <stdexcept>
using namespace std;
class Wektor
{
    double x, y, z;

public:
    Wektor() { x = y = z = 0.0; }           // konstruktor bezargumentowy
    Wektor(double x_, double y_, double z_) // konstruktor z argumentami
    {
        x = x_;
        y = y_;
        z = z_;
    }
    Wektor operator+(const Wektor &) const;
    friend ostream &operator<<(ostream &wyj, const Wektor &vv);
    friend istream &operator>>(istream &wej, Wektor &vv);
};
Wektor Wektor::operator+(const Wektor &v2) const
{
    Wektor v3;
    v3.x = x + v2.x;
    v3.y = y + v2.y;
    v3.z = z + v2.z;
    return v3;
}
ostream &operator<<(ostream &wyj, const Wektor &vv)
{
    wyj << "(" << vv.x << ", " << vv.y << ", " << vv.z << ")";
    return wyj;
}
istream &operator>>(istream &wej, Wektor &vv)
{
    wej >> vv.x >> vv.y >> vv.z;
    return wej;
}
//           %%%%%%%%%%           KLASA MATRIX           %%%%%%%%%%
class Matrix
{
    Wektor **M; // prywatny wskaznik do wskaznika
    int row, col;

public:
    Matrix(int, int); // konstruktor
    ~Matrix();        // destruktor

    int RowSize() { return row; }
    int ColSize() { return col; }
    Wektor &elem(int, int);
    void operator=(Wektor);
    Matrix operator+(const Matrix &) const;
    friend ostream &operator<<(ostream &, Matrix &);
};
Matrix::Matrix(int New_row, int New_col)
{
    row = New_row;
    col = New_col;
    M = new Wektor *[row];
    if (!M)
    {
        throw bad_alloc(); // Rzuć wyjątek, gdy brakuje pamięci
    }

    for (int i = 0; i < row; i++)
    {
        M[i] = new Wektor[col];
        if (!M[i])
        {
            throw bad_alloc();
        }

        for (int j = 0; j < col; j++)
        {
            Wektor V;
            M[i][j] = V;
        }
    }
}
Matrix::~Matrix()
{
    for (int i = 0; i < row; i++)
        delete[] M[i];
    delete[] M;
}
Wektor &Matrix::elem(int rw, int cl)
{
    if (rw < 0 || rw >= row || cl < 0 || cl >= col)
    {
        throw out_of_range("ERROR: Row or column out of size in 'elem()'");
    }
    return M[rw][cl];
}
void Matrix::operator=(Wektor a)
{
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            M[i][j] = a;
}
Matrix Matrix::operator+(const Matrix &N) const
{
    if (row != N.row || col != N.col)
    {
        throw invalid_argument("Macierze mają różne rozmiary");
    }
    Matrix S(row, col);
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            S.M[i][j] = M[i][j] + N.M[i][j];
    return S;
}
ostream &operator<<(ostream &os, Matrix &A)
{
    int rw = A.RowSize();
    int cl = A.ColSize();

    os << "\n";
    for (int i = 0; i < rw; i++)
    {
        for (int j = 0; j < cl; j++)
            os << A.elem(i, j) << "\t";
        os << "\n";
    }
    return os;
}
main()
{
    Matrix M1(2, 2), M2(2, 2), M3(2, 2), M4(2, 2);
    Wektor V1(12, 14, 25), V2(11, 22, 99), V3(10, 20, 30), V4(0, 0, 0);
    M1.elem(0, 0) = V1;
    M1.elem(0, 1) = V2;
    M1.elem(1, 0) = V3;
    M1.elem(1, 1) = V3;
    M2.elem(0, 0) = V3;
    M2.elem(0, 1) = V2;
    M2.elem(1, 0) = V3;
    M2.elem(1, 1) = V1;
    M3 = M1 + M2;
    cout << M1;
    cout << M2;
    cout << M3;
}

 

komentarz 9 maja 2023 przez Robert Lapins Nowicjusz (150 p.)
UPDATE: zmieniłem w klasie Wektor 'double' na 'int' i pierwszy wynik był losowy, następny już poprawny i kolejne też.
Więc wróciłem do typu 'double' w Wektorze - no i działa!

Dlaczego?

Zmarnowałem parę godzin na szukanie jakiegoś błędu. A błędu w kodzie nie było.
Nawet debugger teraz mówi, że jest wszystko ok, a wcześniej wyrzucał mi błąd na ciało przeciążenia operatora wypisywania Wektora.

1 odpowiedź

+3 głosów
odpowiedź 9 maja 2023 przez adrian17 Ekspert (344,100 p.)
wybrane 10 maja 2023 przez Robert Lapins
 
Najlepsza

Nie wiem jak reszta kodu, ale masz tu ewidentnie błąd w kopiowaniu:

M3 = M1 + M2;

Odpalił się domyślny `operator=` dla macierzy, który po prostu kopiuje wskaźnik M - a chwilę później odpala się destruktor wyniku `M1+M2`, więc M3 wskazuje na zwolnioną pamięć.

Więc powinieneś dodać własny operator= (i konstruktor kopiujący) dla macierzy. To btw nazywa się https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming).

if a class defines any of the following then it should probably explicitly define all three:[1]

A swoją drogą: zgubiłeś typ przy `main`ie; to się w ogóle nie skompiluje w niektórych kompilatorach C++a.

1
komentarz 9 maja 2023 przez j23 Mędrzec (194,920 p.)

Dodam jeszcze, że:

        M[i] = new Wektor[col];
        if (!M[i])
        {
            throw bad_alloc();
        }

to jest bez sensu, bo new domyślnie rzuca wyjątek std::bad_alloc, jeśli alokacja się nie powiedzie.

komentarz 9 maja 2023 przez Robert Lapins Nowicjusz (150 p.)

@adrian17, 

Dziękuję serdecznie za wspomnienie o regule trzech w c++.
Program pisałem na podstawie już istniejących klas i przy usuwaniu niepotrzebnych mi tutaj metod nieświadomie usunąłem pozostałą "dwójkę".

Teraz poprawiłem już kod, lecz nadal pojawia się ten sam problem.
Jest to dla mnie ciekawe, że jeden plik w jednym folderze zwraca dobre wartości (dodałem też odejmowanie macierzy) zarówno w VSC jak i w Embarcadero, ale już inny plik o tej samej zawartości zwraca już losowe wartości.

Załączam poniżej poprawiony kod.

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
#include <iostream>
#include <fstream>
#include <stdexcept>
using namespace std;

//           %%%%%%%%%%           KLASA WEKTOR           %%%%%%%%%%

class Wektor
{
    double x, y, z;

public:
    Wektor() { x = y = z = 0.0; }           // konstruktor bezargumentowy
    Wektor(double x_, double y_, double z_) // konstruktor z argumentami
    {
        x = x_;
        y = y_;
        z = z_;
    }
    Wektor operator+(const Wektor &) const;
    Wektor operator-(const Wektor &) const;
    friend ostream &operator<<(ostream &wyj, const Wektor &vv);
    friend istream &operator>>(istream &wej, Wektor &vv);
};

Wektor Wektor::operator+(const Wektor &v2) const
{
    Wektor v3;
    v3.x = x + v2.x;
    v3.y = y + v2.y;
    v3.z = z + v2.z;
    return v3;
}

Wektor Wektor::operator-(const Wektor &v2) const
{
    Wektor v3;
    v3.x = x - v2.x;
    v3.y = y - v2.y;
    v3.z = z - v2.z;
    return v3;
}

ostream &operator<<(ostream &wyj, const Wektor &vv)
{
    wyj << "(" << vv.x << ", " << vv.y << ", " << vv.z << ")";
    return wyj;
}

istream &operator>>(istream &wej, Wektor &vv)
{
    wej >> vv.x >> vv.y >> vv.z;
    return wej;
}

//           %%%%%%%%%%           KLASA MATRIX           %%%%%%%%%%

class Matrix
{
    Wektor **M; // prywatny wskaznik do wskaznika
    int row, col;

public:               // RULE OF THREE C++  -  with capital letters
    Matrix(int, int); // konstruktor
    Matrix(Matrix &); // COPY CONSTRUCTOR
    // Matrix(const Matrix &N) : M(N.M) {} // COPY CONSTRUCTOR alternative
    ~Matrix(); // DESTRUCTOR

    int RowSize() const { return row; }
    int ColSize() const { return col; }
    Wektor &elem(int, int);
    void operator=(Wektor);
    Matrix &operator=(const Matrix &); // COPY ASSIGNMENT OPERATOR
    Matrix operator+(const Matrix &) const;
    Matrix operator-(const Matrix &) const;
    friend ostream &operator<<(ostream &, Matrix &);
};

// RULE OF THREE C++  -  with capital letters
Matrix::Matrix(Matrix &N) // 1. COPY CONSTRUCTOR
{
    row = N.row;
    col = N.col;
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            elem(i, j) = N.elem(i, j);
    // M[i][j] = N.M[i][j];
    // M[i][j] = N.elem(i, j);
}

Matrix &Matrix::operator=(const Matrix &N) // 2. COPY ASSIGNMENT OPERATOR
{
    if (this != &N)
        M = N.M;
    return *this;
}

Matrix::~Matrix() // 3. DESTRUCTOR
{
    for (int i = 0; i < row; i++)
        delete[] M[i];
    delete[] M;
}

Matrix::Matrix(int New_row, int New_col)
{
    row = New_row;
    col = New_col;
    M = new Wektor *[row];
    for (int i = 0; i < row; i++)
    {
        M[i] = new Wektor[col];
        for (int j = 0; j < col; j++)
        {
            Wektor V;
            M[i][j] = V;
        }
    }
}

Wektor &Matrix::elem(int rw, int cl)
{
    if (rw < 0 || rw >= row || cl < 0 || cl >= col)
    {
        throw out_of_range("ERROR: Row or column out of size in 'elem()'");
    }
    return M[rw][cl];
}

void Matrix::operator=(Wektor a)
{
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            M[i][j] = a;
}

Matrix Matrix::operator+(const Matrix &N) const
{
    if (row != N.row || col != N.col)
    {
        throw invalid_argument("Macierze mają różne rozmiary");
    }
    Matrix S(row, col);
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            S.M[i][j] = M[i][j] + N.M[i][j];
    return S;
}

Matrix Matrix::operator-(const Matrix &N) const
{
    if (row != N.row || col != N.col)
    {
        throw invalid_argument("Macierze mają różne rozmiary");
    }
    Matrix S(row, col);
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            S.M[i][j] = M[i][j] - N.M[i][j];
    return S;
}

ostream &operator<<(ostream &os, Matrix &A)
{
    int rw = A.RowSize();
    int cl = A.ColSize();

    os << "\n";
    for (int i = 0; i < rw; i++)
    {
        for (int j = 0; j < cl; j++)
            os << A.elem(i, j) << "\t";
        os << "\n";
    }
    return os;
}

main()
{
    Matrix M1(2, 2), M2(2, 2), M3(2, 2), M4(2, 2);
    Wektor V1(120.55, 14, 25), V2(11, 22, 99), V3(10, 20, 30), V4(0, 0, 0);
    M1.elem(0, 0) = V1;
    M1.elem(0, 1) = V2;
    M1.elem(1, 0) = V3;
    M2.elem(0, 1) = V2;
    M2.elem(1, 0) = V3;
    M2.elem(1, 1) = V1;
    M3 = M1 + M2;
    M4 = M1 - M2;
    cout << M1;
    cout << M2;
    cout << M3;
    cout << M4;

    return 0;
}

 

komentarz 9 maja 2023 przez j23 Mędrzec (194,920 p.)
edycja 9 maja 2023 przez j23

To chyba było generowane w ChatGPT, co? :P

Konstruktor kopiujący masz źle napisany. Tak może być:

Matrix::Matrix(const Matrix &matrix) 
{
    row = matrix.row;
    col = matrix.col;

    M = new Wektor*[row];

    for (int r = 0; r < row; r++)
        M[r] = new Wektor[col]
        for (int c = 0; c < col; c++)
            M[r][c] = matrix.M[r][c];
}
 
Matrix &Matrix::operator=(Matrix matrix) 
{
    std::swap(M, matrix.M);
    std::swap(row, matrix.row);
    std::swap(col, matrix.col);
    return *this;
}

Dodaj też Wektor elem(int, int) const, bo teraz nie ma dostępu do wartości macierzy, jeśli ta jest const.

komentarz 9 maja 2023 przez adrian17 Ekspert (344,100 p.)

Matrix &Matrix::operator=(Matrix matrix)

{

    std::swap(M, matrix.M);

To by miało sens gdyby to był move, ale nie przy zwykłym przypisaniu, nie?

komentarz 10 maja 2023 przez Robert Lapins Nowicjusz (150 p.)
edycja 10 maja 2023 przez Robert Lapins

Super, teraz działa poprawnie! Dziękuję Wam!
Tak, skorzystałem z ChatGPT, bo poprosiłem go o uproszczenie kodu angry.

Dla konstruktora kopiującego wystarczyła jedna linijka:

Matrix(const Matrix &N) : M(N.M) {} // COPY CONSTRUCTOR

A dla przeciążenia operatora kopiującego trzeba więcej:

Matrix &Matrix::operator=(const Matrix &N) // 2. COPY ASSIGNMENT OPERATOR
{
    if (this != &N)
    {
        for (int i = 0; i < row; i++)
            delete[] M[i];
        delete[] M;

        int rw = this->RowSize(); // this wskazuje na N
        int cl = N.ColSize();

        M = new Wektor *[rw];
        for (int i = 0; i < rw; i++)
            M[i] = new Wektor[cl];

        for (int i = 0; i < rw; i++) // kopiujemy tablice
            for (int j = 0; j < cl; j++)
                M[i][j] = N.M[i][j];
    }
    return *this;
}

Chociaż w tym fragmencie nie rozumiem, dlaczego wskaźnik 'this' ma wskazywać akurat na N zaraz po usunięciu M (choć, wiem, że można inaczej to zapisać).
Jeszcze raz dziękuję i kłaniam się!

komentarz 10 maja 2023 przez j23 Mędrzec (194,920 p.)

@adrian17, zwróć uwagę, że argument jest przekazywany przez wartość, ergo - kopiowanie następuje w konstruktorze kopiującym, a w operatorze następuje przeniesienie tej kopii do obiektu this. Destruktor parametru usuwa starą tablicę i wszystko gra i trąbi. Ma to nawet swoją nazwę: copy and swap idiom.

@Robert Lapins

Dla konstruktora kopiującego wystarczyła jedna linijka:

Nie, to jest błąd. Konstruktor kopiujący zgodnie z nazwą powinien zrobić kopię tej tablicy. A to, co tam zrobiłeś, robi owszem kopię, ale wskaźnika. Efekt: dwa obiekty posiadają tę samą pamięć, co oznacza, że w destruktorze ta pamięć jest usuwana dwa lub więcej razy.

A dla przeciążenia operatora kopiującego trzeba więcej:

Nie wiem dlaczego nie zastosowałeś kodu, który podałem. Tam masz wszystko zrobione tak, jak trzeba.

Tak, skorzystałem z ChatGPT, bo poprosiłem go o uproszczenie kodu angry.

Testuje ostatnio ChatGPT i inne przy pisaniu drobiazgów w Pythonie i powiem tak: tutaj kluczowe jest to, jak szczegółowy jest prompt. Na przykład poprosiłem w Bingu, by napisał mi metody zgodne z rule of three pisząc:

write methods according to the rule of three for the Matrix class, use the copy and swap idiom. M is the pointer to 2d array. Here is the class:

class Matrix {

Wektor **M; 

int row, col;

};

W odpowiedzi dostałem to:

To implement the rule of three in the Matrix class, you need to define a user-defined destructor, a user-defined copy constructor, and a user-defined copy assignment operator1The copy-and-swap idiom is a technique that can be used to implement the rule of three2It is used to provide a strong exception guarantee and to avoid code duplication2. Here is an example of how you can use the copy-and-swap idiom in your Matrix class:

class Matrix {
    Wektor **M;
    int row, col;
public:
    Matrix(int r, int c) : row(r), col(c) {
        M = new Wektor*[row];
        for (int i = 0; i < row; ++i) {
            M[i] = new Wektor[col];
        }
    }
    ~Matrix() {
        for (int i = 0; i < row; ++i) {
            delete[] M[i];
        }
        delete[] M;
    }
    Matrix(const Matrix& other) : Matrix(other.row, other.col) {
        for (int i = 0; i < row; ++i) {
            for (int j = 0; j < col; ++j) {
                M[i][j] = other.M[i][j];
            }
        }
    }
    Matrix& operator=(Matrix other) {
        std::swap(row, other.row);
        std::swap(col, other.col);
        std::swap(M, other.M);
        return *this;
    }
};

In this example, the copy constructor creates a new object with the same dimensions as the original object and copies all elements from the original object to the new object. The copy assignment operator uses the copy-and-swap idiom to create a temporary copy of the right-hand side object and swaps its contents with those of the left-hand side object2.

I hope this helps!

Kod wygląda na poprawny.

Druga rzecz, traktuj odpowiedź czata jako sugestie, a nie rozwiązanie (czasami warto go spytać, czy jest pewny swojej odpowiedzi)

komentarz 10 maja 2023 przez Robert Lapins Nowicjusz (150 p.)

@j23,  

Dobrze, już zrozumiałem koncepcję rule-of-three, dzięki zaznajomieniu się z copy-and-swap.

Nie wiem dlaczego nie zastosowałeś kodu, który podałem. Tam masz wszystko zrobione tak, jak trzeba.

Nie zastosowałem, bo nie działało, ponieważ skopiowałem tylko ciało operatora kopiowania (same swapy), więc wróciłem do modyfikacji rozwiązania pierwotnego kodu.
Ale teraz już wiem już, że do copy-and-swap nie mogę użyć kwalifikatora 'const' ani wskaźnika '&' do prawego argumentu operatora kopiowania, ze względu na operacje, na które się składa.

Nie, to jest błąd. Konstruktor kopiujący zgodnie z nazwą powinien zrobić kopię tej tablicy. A to, co tam zrobiłeś, robi owszem kopię, ale wskaźnika. Efekt: dwa obiekty posiadają tę samą pamięć, co oznacza, że w destruktorze ta pamięć jest usuwana dwa lub więcej razy.

Trochę po napisaniu poprzedniego komentarza poprosiłem AI o wytłumaczenie mi tej jednej linijki konstruktora kopiującego i w odpowiedzi już stwierdził, że mam tzw. kopiowanie płytkie i podał mi jak napisać tzw. kopiowanie głębokie (jak twoje).

Myślę, że sporo się nauczyłem przez ogarnięcie tego przykładu i już załapałem tu obecne podstawy C++.

Poniżej zamieszczam dla potwierdzenia ostateczną wersję kodu:
 

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
#include <iostream>
#include <fstream>
#include <stdexcept>
using namespace std;

//           %%%%%%%%%%           KLASA WEKTOR           %%%%%%%%%%

class Wektor
{
    double x, y, z;

public:
    Wektor() { x = y = z = 0.0; }
    Wektor(double x_, double y_, double z_) : x(x_), y(y_), z(z_) {}

    Wektor operator+(const Wektor &) const;
    Wektor operator-(const Wektor &) const;
    friend ostream &operator<<(ostream &wyj, const Wektor &vv);
    friend istream &operator>>(istream &wej, Wektor &vv);
};

Wektor Wektor::operator+(const Wektor &v2) const
{
    Wektor v3;
    v3.x = x + v2.x;
    v3.y = y + v2.y;
    v3.z = z + v2.z;
    return v3;
}

Wektor Wektor::operator-(const Wektor &v2) const
{
    Wektor v3;
    v3.x = x - v2.x;
    v3.y = y - v2.y;
    v3.z = z - v2.z;
    return v3;
}

ostream &operator<<(ostream &wyj, const Wektor &vv)
{
    wyj << "(" << vv.x << ", " << vv.y << ", " << vv.z << ")";
    return wyj;
}

istream &operator>>(istream &wej, Wektor &vv)
{
    wej >> vv.x >> vv.y >> vv.z;
    return wej;
}

//           %%%%%%%%%%           KLASA MATRIX           %%%%%%%%%%

class Matrix
{
    Wektor **M; // prywatny wskaznik do wskaznika (tablica 2d)
    int row, col;

public:                      // RULE OF THREE C++  -  with capital letters
    Matrix(int, int);        // konstruktor
    Matrix(const Matrix &N); // 1. COPY CONSTRUCTOR
    ~Matrix();               // 2. DESTRUCTOR

    Wektor &elem(int, int);
    void operator=(Wektor);
    Matrix &operator=(Matrix); // 3. COPY ASSIGNMENT OPERATOR
    Matrix operator+(const Matrix &) const;
    Matrix operator-(const Matrix &) const;
    friend ostream &operator<<(ostream &, Matrix &);
};

Matrix::Matrix(int r, int c) : row(r), col(c) // konstruktor
{
    M = new Wektor *[row];
    for (int i = 0; i < row; i++)
    {
        M[i] = new Wektor[col];
        for (int j = 0; j < col; j++)
        {
            Wektor V;
            M[i][j] = V;
        }
    }
}

// RULE OF THREE C++  -  with capital letters
Matrix::Matrix(const Matrix &N) : Matrix(N.row, N.col) // 1. COPY CONSTRUCTOR
{
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            M[i][j] = N.M[i][j];
}

Matrix::~Matrix() // 2. DESTRUCTOR
{
    for (int i = 0; i < row; i++)
        delete[] M[i];
    delete[] M;
}

Matrix &Matrix::operator=(Matrix N) // 3. COPY ASSIGNMENT OPERATOR
{
    std::swap(M, N.M); // copy-and-swap idiom
    swap(row, N.row);
    swap(col, N.col);
    return *this;
}
/*
Aby użyć idiomu copy-and-swap, potrzebujemy trzech rzeczy:
działającego konstruktora kopii, działającego destruktora
(oba są podstawą każdego wrappera, więc powinny być kompletne)
oraz funkcji zamiany.
Tworzy kopię(prawego argumentu), podmienia wartości starego z kopią,
usuwa kopię(ze starymi wartościami).
*/

Wektor &Matrix::elem(int rw, int cl)
{
    if (rw < 0 || rw >= row || cl < 0 || cl >= col)
    {
        throw out_of_range("ERROR: Row or column out of size in 'elem()'");
    }
    return M[rw][cl];
}

void Matrix::operator=(Wektor a)
{
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            M[i][j] = a;
}

Matrix Matrix::operator+(const Matrix &N) const
{
    if (row != N.row || col != N.col)
    {
        throw invalid_argument("Macierze mają różne rozmiary");
    }
    Matrix S(row, col);
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            S.M[i][j] = M[i][j] + N.M[i][j];
    return S;
}

Matrix Matrix::operator-(const Matrix &N) const
{
    if (row != N.row || col != N.col)
    {
        throw invalid_argument("Macierze mają różne rozmiary");
    }
    Matrix S(row, col);
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            S.M[i][j] = M[i][j] - N.M[i][j];
    return S;
}

ostream &operator<<(ostream &os, Matrix &A)
{
    int rw = A.row;
    int cl = A.col;

    os << "\n";
    for (int i = 0; i < rw; i++)
    {
        for (int j = 0; j < cl; j++)
            os << A.elem(i, j) << "\t";
        os << "\n";
    }
    return os;
}

main()
{
    Matrix M1(2, 2), M2(2, 2), M3(2, 2), M4(2, 2);
    Wektor V1(120.55, 14, 25), V2(11, 22, 99), V3(10, 20, 30), V4(0, 0, 0);
    M1.elem(0, 0) = V1;
    M1.elem(0, 1) = V2;
    M1.elem(1, 0) = V3;
    M2.elem(0, 1) = V2;
    M2.elem(1, 0) = V3;
    M2.elem(1, 1) = V1;
    M3 = M1 + M2;
    M4 = M1 - M2;
    cout << M1;
    cout << M2;
    cout << M3;
    cout << M4;
    return 0;
}

Druga rzecz, traktuj odpowiedź czata jako sugestie, a nie rozwiązanie (czasami warto go spytać, czy jest pewny swojej odpowiedzi)

Cenna uwaga :)
Dziękuję Wam za tak obszerne komentarze.

komentarz 10 maja 2023 przez j23 Mędrzec (194,920 p.)
Pętla z linii 82...86 jest redundantna - usuń ją.
komentarz 12 maja 2023 przez Robert Lapins Nowicjusz (150 p.)

Dziękuję za uwagę! heart

Rozumiem, że już ta linia tuż poniżej

M[i] = new Wektor[col];

rezerwując pamięć na stercie, uruchamia konstruktor Wektora dla col-elementów dla każdego i-wiersza, to jest w sumie logiczne.

komentarz 13 maja 2023 przez j23 Mędrzec (194,920 p.)
Tak.

Podobne pytania

0 głosów
0 odpowiedzi 207 wizyt
–1 głos
2 odpowiedzi 1,989 wizyt
pytanie zadane 9 stycznia 2016 w C i C++ przez songo0 Początkujący (300 p.)
0 głosów
2 odpowiedzi 256 wizyt
pytanie zadane 20 marca 2018 w C i C++ przez lukasz9819 Początkujący (360 p.)

92,452 zapytań

141,262 odpowiedzi

319,085 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...