Wskaźnik do dynamicznej tablicy paczek

0

Proszę o jakieś ukierunkowanie gdzie jest błędne myślenie.
Krótki opis o co chodzi, i co się dzieje przy poszczególnych zmianach.
Tuż pod mainem znajduje sie 6 elementowa tablica typu Paczka.
Klasa kurier posiada jako składnik m.in wskaźnik na obiekt typu Paczka. W tym przykładzie wskaźnik wskazuje na pierwszy element dynamicznej tablicy Paczek. Drugim składnikiem Kuriera jest int ilosc_ jako rozmiar tablicy.
Gdy zakomentuje linie 57-60 to wyswietla sie cout << --------12---------;
Natomiast gdy nie będzie zakomentowany to już się nie wyświetla '12' .
Sprawdzałem w debuggerze oraz operatorem&, adresy wskaźnika 'paczki_' w klasie kurier nie dzielą między sobą wspólnych adresów.

Szybki spis treści:
Linia 53: konstruktor kopiujacy
85: operator przypisania kopiującego
180-196: Wywołania które mają potencjalny wpływ na te problemy.

Będę wdzięczny za każdą pomoc :)

Ps. "Czemu nie używasz czegoś zamiast tego ?" - Bo tak nas na ten moment uczą i chciałbym to opanować, na wszystko będzie czas.

#include <iostream>
using namespace std;
//Pojazd(const Pojazd& p)
//operator=(const pojazd& p)
class Paczka
{
private:
	string adres_;
	float wartosc_;
public:
	Paczka(){};
	~Paczka(){};
	Paczka (string adres_, float wartosc_)
	{
		this->adres_ = adres_;
		this->wartosc_ = wartosc_;
	}

	string &adres(){return adres_;}
	float &wartosc(){return wartosc_;}
	friend ostream& operator<<(ostream& os, Paczka& p);
};
ostream& operator<<(ostream& os, Paczka& p)
	{
		os << "adres dostawy: " << p.adres_ << '\t' << "wartosc paczki: " << p.wartosc_ << endl;
		return os;
	}
class Osoba
{
private:
	string imie_;
	string nazwisko_;
public:
	Osoba(){this-> imie_ = "Brak"; this-> nazwisko_ = "Brak";};
	~Osoba(){};
	Osoba(string imie_, string nazwisko_)
	{
		this->imie_ = imie_;
		this->nazwisko_ = nazwisko_;
	}
	friend ostream &operator<<(ostream& os, Osoba& o);
	friend bool operator==(const Osoba& a, const Osoba& b);
	string &imie(){return imie_;}
	string &nazwisko(){return nazwisko_;}
};
class Kurier : public Osoba
{
private:
    Paczka* paczki_;
    int ilosc_;
public:
    Kurier(){paczki_ = 0; ilosc_ = 0;}
    Kurier(Kurier& k) //KONSTURKTOR KOPIUJACY
    {
        this->paczki_ = new Paczka [k.ilosc_];
        this->ilosc_ = k.ilosc_;
        for(int i = 0; i < ilosc_; i++)
        {
            paczki_ = k.paczki_;
        }
    }
    ~Kurier(){if(paczki_!=0){delete [] paczki_;}}
    Kurier(Paczka* poczatek, Paczka* koniec)
    {
        ilosc_ = koniec - poczatek;
        paczki_ = new Paczka [ilosc_];
        for (int i = 0; i < ilosc_; ++i)
        {
            paczki_[i] = poczatek[i];
        }
    }
    float wartosc() // cout << k1.wartosc() << endl;
    {
            float tempWartosc = 0;
    if(paczki_ != 0)
        {
            for(int i = 0; i < ilosc_; ++i)
            {
                tempWartosc += paczki_[i].wartosc();
            }
            return tempWartosc;
        }
        else return 0;
    }
Kurier &operator=(Kurier& k) //OPERATOR PRZYPISANIA KOPIUJACEGO
{
    if(this != &k){delete [] paczki_;
    ilosc_ = k.ilosc_;
    paczki_ = new Paczka[ilosc_];
    for (int i = 0; i < ilosc_; ++i)
    {
        paczki_[i] = k.paczki_[i];
        cout << &paczki_[i] << " " << &k.paczki_[i] << " | ";
    }
    }
    return *this;
}//k1 = k1 + p[4];
Kurier &operator+(Paczka& p) //DODAWANIE PACZEK
{
    Paczka* temp = new Paczka[ilosc_+ 1];
    for (int i = 0; i < ilosc_; ++i)
    {
        temp[i] = paczki_[i];
    }
    temp[ilosc_] = p;
    delete [] paczki_;
    paczki_ = new Paczka[ilosc_+ 1];
    for (int i = 0; i < ilosc_+ 1; ++i)
    {
        paczki_[i] = temp[i];
    }
    delete [] temp;
    ilosc_++;
    return *this;
}
Paczka &operator[](int &num) //ZWRACA INFORMACJE O PACZCE SPOD PODANEGO INDEKSU
{
    if(num < 0 || num > ilosc_)
    {
        throw -10;
    }

    else{return paczki_[num];}
}
};
bool operator==(const Osoba& a, const Osoba& b)
    {return a.imie_ == b.imie_ && a.nazwisko_ == b.nazwisko_;}
ostream &operator<<(ostream& os, Osoba& o)
{
	os << "imie: " << o.imie_ << '\t' << "nazwisko: " << o.nazwisko_ << endl;
	return os;
}

int main()
{
  Paczka p[] = {
    Paczka("Dabrowskiego 33", 123.00),
    Paczka("NMP 12", 40.00),
    Paczka("Armii Krakowej 4", 30.00),
    Paczka("Andersa 6", 51.00),
    Paczka("Kukuczki 13", 12.00),
    Paczka("Skrzyneckiego 5", 40.00),
  };

  Osoba o1("Jan", "Kowalski");
  cout << "---- 1 ----" << endl;
  cout << o1 << endl;

  {
      Osoba o2(o1);
      cout << "---- 2 ----" << endl;
      cout << o2 << endl;

      cout << "---- 3 ----" << endl;
      cout << boolalpha << (o1 == o2) << endl;

      Osoba o3;
      cout << "---- 4 ----" << endl;
      cout << o3 << endl;

      o3 = o2;
      cout << "---- 5 ----" << endl;
      cout << o3 << endl;

  }

  cout << "---- 6 ----" << endl;
  cout << o1 << endl;

  Kurier k1(p, p+3);//wskazniki (p+3 to pierwsza paczka z listy ktora nie ma byc 'kopiowana')
  cout << "---- 7 ----" << endl;
  cout << k1;
  cout << k1.wartosc() << endl;

 Kurier k2;
  cout << "---- 8 ----" << endl;
  cout << k2.wartosc() << endl;

  {
    Kurier k3(k1);
    cout << "---- 9 ----" << endl;
    cout << k3 << endl;

    k2 = k3;
    cout << "---- 10 ----" << endl;
    cout << k2.wartosc() << endl;

    k1 = k1 + p[4];
    k1 = k1 + p[5];

    cout << "---- 11 ----" << endl;
    cout << k1.wartosc() << endl;
  }

  cout << "---- 12 ----" << endl;
  cout << k2.wartosc() << endl;
 /* try{
    Kurier k4(p, p+3);
    cout << "---- 13 ----" << endl;
    for(int i=0; i<10; ++i){
      cout << k4[i] << ",";
    }
    cout << endl;
  }
  catch(...){
    cout << "---- 14 ----" << endl;
    cout << "Wyjatek" << endl;

  }*/
  return 0;
}

2

Ciężko mi samemu na to odpowiedzieć, bo według mnie powinien działać ten program. Mówiąc szczerze to oczekuję analizy w zakresie przydzielania pamięci przy np. konstruktorze kopiującym, operatorze przypisania. Nie jestem w stanie określić konkretnej przyczyny problemu, bo jej nie widzę ;/ Innego wyjścia chyba nie ma.

a ja oczekuję że użyjesz:

  1. GDB
  2. Valgrind(i jego "podnarzędzia np. mem check")
  3. adress sanitizer

Do wszystkich 3 na yt instrukcje jak używać. Niestety nie mam czasu na analizę a i sam nauczysz się więcej. Jak będzie jakiś nians to wróć to się wtedy looknie.

4

Tu masz ten sam błąd co wyżej (zminimalizowałem przykład)

#include <iostream>
using namespace std;

struct Paczka {
    string adres_ = "";
    float wartosc_ = 0;
};

class Kurier {
    Paczka* paczki_ = nullptr;
    size_t ilosc_ = 0;
public:
    Kurier(const Kurier& k) {
        paczki_ = new Paczka [k.ilosc_];
        ilosc_ = k.ilosc_;
        for (size_t i = 0; i < ilosc_; i++) {
            paczki_ = k.paczki_;
        }
    }
    ~Kurier() {
        if (paczki_ != nullptr) {
            delete [] paczki_;
        }
    }
    Kurier(Paczka* poczatek, Paczka* koniec) {
        ilosc_ = static_cast<size_t>(koniec - poczatek);
        paczki_ = new Paczka [ilosc_];
        for (size_t i = 0; i < ilosc_; ++i) {
            paczki_[i] = poczatek[i];
        }
    }
};

int main() {
    Paczka p[] = {
        Paczka{"Dabrowskiego 33", 123.00},
        Paczka{"NMP 12", 40.00},
        Paczka{"Armii Krakowej 4", 30.00},
    };

    Kurier k1(p, p+3);
    Kurier k2(k1);
    return 0;
}
5

w ogóle odpaliłem ten program, i chyba nie czytasz co tam ci wychodzi na wyjściu i to chyba dużo podpowie. Jak to puścisz w GDB to bajka powinna być

Program returned: 139
free(): double free detected in tcache 2

do tego weź sobie odpal statyczną analizę kodu jak cppcheck czy clang-tidy.
edit:
4p_debug.png

0

Dałeś 25 cout w main a prawie żadnego w funkcjach. Program wg mnie, powinien na początkowym etapie (kiedy jeszcze się uczysz) generować logi po każdym działaniu.

Kurier &operator+(Paczka& p) //DODAWANIE PACZEK
{
    Paczka* temp = new Paczka[ilosc_+ 1];
    for (int i = 0; i < ilosc_; ++i)
    {
        temp[i] = paczki_[i];
    }
    temp[ilosc_] = p;
    delete [] paczki_;
    paczki_ = new Paczka[ilosc_+ 1];
    for (int i = 0; i < ilosc_+ 1; ++i)
    {
        paczki_[i] = temp[i];
    }
    delete [] temp;
    ilosc_++;
    return *this;
}```

Tutaj nie masz ani jednego.

0

Okej, valgrind mi za bardzo nie pomógł, bo po prostu nie umiem z niego jeszcze korzystać. Debugger nakierował mnie na linie 53 gdzie jest konstruktor kopiujący. Aż głupio się przyznać ale w linii 59 brakuje indeksów ;p więc cały czas była przepisywana pierwsza paczka z jednego kuriera do pierwszej paczki drugiego kuriera. Chociaż nie potrafię sobie zobrazować dlaczego spowodowało to wyciek.

0

operator=?

0

Jakby ktoś mógł mi powiedzieć czemu brak indeksu w 59 linii spowodował wyciek pamięci, to byłbym wdzięczny :) W moim rozumowaniu, zostaje przydzielona pamięć na 'n' elementów, ale używany jest tylko pierwszy. Co nie zmienia faktu, że dostęp to całego obszaru pamięci nadal jest dostępny poprzez wskaźnik typu paczka, więc przypuszczam, że powinien zostać usunięty wraz z destruktorem.

0
    Kurier(const Kurier& k) {
        paczki_ = new Paczka [k.ilosc_];
        ilosc_ = k.ilosc_;
        for (size_t i = 0; i < ilosc_; i++) {
            // tutaj nadpisujesz przed chwilą zaalokowany wskaźnik, przez co nigdy go nie zwolnisz
            paczki_ = k.paczki_;
        }
    }

ale większy zarzut to oczywiście, że potem zarówno k jak i podmiot danego konstruktora będzie zwalniać tę samą pamięć.

0

Dodałbym jeszcze, że masz jeszcze jednen problem:

Kurier k1(p, p+3);
cout << k1 << "\n";
k1 + p;
cout << k1 << "\n";

Wyjaśnij mi proszę, dlaczego uważasz za rozsądne, że te dwie wartości będą różne.

Już nie wspominając o tym że ta implementacja dodawania jest całkiem niewydajna - zawsze używa czasu proporcjonalnego do liczby dotychczasowych paczek.

edit:
chodziło mi o taki przykład:

  Kurier k1(p, p+3);
  cout << k1.wartosc() << "\n";
  k1 + p[0];
  cout << k1.wartosc() << "\n";

1 użytkowników online, w tym zalogowanych: 0, gości: 1