Listy dwukierunkowe C++

0

Witam. Jestem w trakcie robienia mini projektu dotyczącego czytelników, książek i ich wypożyczania. Zamieszczę jedynie fragment kodu, w którym występuje problem oraz pozostały kod do uruchomienia programu. Problem tkwi w poprawnym wydrukowaniu czytelników tzn: imię, nazwisko oraz dla każdego (dwukierunkowa) lista wypożyczonych książek. W main'ie kod pokazuje, że funkcja print(lista) działa poprawnie. Natomiast gdy chce za pomocą funkcji wyświetlić grupę czytelników, która korzysta z funkcji print(lista), program się wykrzacza. Najciekawsze jest to, że dla jednego czytelnika wszystko działa. Natomiast gdy do grupy dodam kolejnego czytelnika to wtedy jest błąd. Czy ktoś mógłby mi pomóc?

#include <iostream>
#include <cstdlib>

using namespace std;

class Ksiazka {
private:
    Ksiazka *pop, *nast;

public:
    string tytul;

    Ksiazka* getPop() { return pop; }
    Ksiazka* getNast() { return nast; }
    void setPop(Ksiazka* NewPop) { pop = NewPop; }
    void setNast(Ksiazka* NewNast) { nast = NewNast; }

    void pobierzK()
    {
        cout << "Podaj tytul ksiazki" << endl;
        cin >> tytul;
    }

    Ksiazka(){};
    //Ksiazka(){pop=NULL;nast=NULL;tytul="";}
    Ksiazka(string SETtytul)
    {
        pop = NULL;
        nast = NULL;
        tytul = SETtytul;
    }
    Ksiazka(Ksiazka* R) { tytul = R->tytul; };

    void wydruk()
    {
        //if(tytul!="")
        cout << "Tytul: " << tytul << endl;
    }
}; 
 
class Lista {
public:
    Ksiazka *pierwszy, *ostatni;
    int ilosc;
    Lista()
    {
        pierwszy = NULL;
        ostatni = NULL;
        ilosc = 0;
    }
    Lista(Lista* M)
    {
        pierwszy = M->pierwszy;
        ostatni = M->ostatni;
        ilosc = M->ilosc;
    }
    //Lista(string nazwa1) {pierwszy=NULL; ostatni=NULL; ilosc=0; nazwa=nazwa1;}

    ~Lista()
    {
        Ksiazka* E = pierwszy;
        Ksiazka* Temp;
        while (E) {
            Temp = E;
            E = Temp->getNast();
            delete Temp;
        }
    }

    void Add2Begin(Ksiazka* E)
    {
        if (pierwszy) {
            pierwszy->setPop(E);
            E->setNast(pierwszy);
            pierwszy = E;
            ilosc++;
        }

        else {
            pierwszy = E;
            ostatni = E;
            ilosc = 1;
            //pierwszy->setPop(NULL);
            //pierwszy->setNast(NULL);
        }
    }
};

void print(Lista* M)
{
    Ksiazka* Temp;
    Temp = M->pierwszy;
    if (Temp == NULL) {
        cout << endl;
        cout << "Brak elementow" << endl;
    }
    else {
        cout << "ilosc: " << M->ilosc << endl;
        while (Temp) {
            Temp->wydruk();
            Temp = Temp->getNast();
        };
    }
}  
 
class Czytelnik : public Ksiazka, Lista {
public:
    string Imie;
    string Nazwisko;
    Lista* K;
    Czytelnik()
    {
        Imie = "";
        Nazwisko = "";
        K = NULL;
    };
    Czytelnik(string imie, string nazwisko, string TYTUL)
    {
        Imie = imie;
        Nazwisko = nazwisko;
        tytul = TYTUL;
    };
    Czytelnik(string imie, string nazwisko, Lista* R)
    {
        Imie = imie;
        Nazwisko = nazwisko;
        K = R;
    };

    void pobierz()
    {
        cout << "Podaj imie" << endl;
        cin >> Imie;

        cout << "Podaj nazwisko" << endl;
        cin >> Nazwisko;
    }
};

class grupa : public Czytelnik {
public:
    int ilosc;
    Czytelnik* ekipa;

    grupa()
    {
        ekipa = NULL;
        ilosc = 0;
    }

    void wyswietl()
    {
        if (ekipa == NULL) {
            cout << "lista czytelnikow jest pusta" << endl;
        }
        else {
            cout << "ilosc: " << ilosc << endl;
            for (int i = 0; i < ilosc; i++) {
                cout << i + 1 << "."
                     << "Imie: " << ekipa[i].Imie << " Nazwisko: " << ekipa[i].Nazwisko << " \nWypozyczone ksiazki: " << endl;
                cout << "ok przed lista";

                //ekipa[i].K->print();

                print(ekipa[i].K);

                //K->print();
                cout << "ok po liscie";
            }
        }
    }
};

void dodaj(grupa* G, Czytelnik* C)
{
    C->pobierz();
    if (G->ekipa == NULL) {
        G->ekipa = new Czytelnik[1];
        G->ilosc = 1;
        G->ekipa[0].Imie = C->Imie;
        G->ekipa[0].Nazwisko = C->Nazwisko;
        G->ekipa[0].K = new Lista();

        cout << "ok po pustej";
    }

    else {
        Czytelnik* newEkipa = new Czytelnik[G->ilosc + 1]; // nowa tablica, powolywanie do zycia

        for (int i = 0; i < G->ilosc; i++) {
            newEkipa[i].Imie = G->ekipa[i].Imie;
            newEkipa[i].Nazwisko = G->ekipa[i].Nazwisko;
            newEkipa[i].K = G->ekipa[i].K;

            // new Lista(G->ekipa[i].K)

            cout << "ok po przepisaniu";
        }
        G->ilosc++; // ilosc ++

        newEkipa[G->ilosc - 1] = Czytelnik(C->Imie, C->Nazwisko, C->K);

        //newEkipa[G->ilosc-1].Imie=C->Imie; //podmienic osttaniego
        //newEkipa[G->ilosc-1].Nazwisko=C->Nazwisko;//podmienic osttaniego
        //newEkipa[G->ilosc-1].K=new Lista(C);

        //newEkipa[G->ilosc-1].K->pierwszy=C->K->pierwszy;
        //newEkipa[G->ilosc-1].K->ostatni=C->K->ostatni;
        //newEkipa[G->ilosc-1].K->ilosc=C->K->ilosc;

        cout << "ok po ostatnim";

        delete[] G->ekipa; //zwalnianie pamieci
        G->ekipa = newEkipa;
        cout << "ok po usunieciu";
    }
}
 
int main(int argc, char** argv)
{

    grupa* G = new grupa;
    Czytelnik* C = new Czytelnik();
    Lista L;
    Czytelnik* D = new Czytelnik();

    /*
dodaj(G,C); cout << endl;
G->wyswietl(); cout << endl;
 dodaj(G,D);  cout << endl;
G->wyswietl(); cout << endl;   */ // tu jest problem

    {
        Ksiazka* E = new Ksiazka("a");
        L.Add2Begin(E);
    }

    {
        Ksiazka* E = new Ksiazka("b");
        L.Add2Begin(E);
    }

    {
        Ksiazka* E = new Ksiazka("c");
        L.Add2Begin(E);
    }

    {
        Ksiazka* E = new Ksiazka("d");
        L.Add2Begin(E);
    }

    {
        Ksiazka* E = new Ksiazka("e");
        L.Add2Begin(E);
    }

    print(&L);

    {
        Ksiazka* E = new Ksiazka("bbbbbbbb");
        L.Add2Begin(E);
    }

    print(&L);

    system("pause");
    return 0;
}

// Dodam, że jestem nowicjuszem w programowaniu, także liczę na każda krytykę. Pozdrawiam!

4
  1. fatalne formatowanie. Jak sam nie umiesz - http://format.krzaq.cc
  2. słabe nazewnictwo, do tego mieszasz języki. Czym jest pop? w funkcji Add2Begin ma być cool i 2 = to, czy to drugie podejście do pisania funkcji?
  3. poczytaj o dziedziczeniu, bo to co robisz kompletnie nie ma sensu. Prywatnie dziedziczysz po Lista, grupa dziedziczy po Czytelniku. Wtf?
  4. nagie new i delete to antyidiomy w C++, używaj smart pointerów i kontenerów z biblioteki standardowej
  5. Odpal to w debuggerze i zobacz na czym konkretnie się wywala. Próbowałem się wgryźć w kod, ale jest straszny.

Piszesz, że jesteś nowicjuszem. Polecam lekturę: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md (trochę długa, więc pewnie na kilka-kilkanaście razy).

0
kq napisał(a):

fatalne formatowanie. Jak sam nie umiesz - http://format.krzaq.cc

Dziękuję, już poprawiłem.

kq napisał(a):

słabe nazewnictwo, do mieszasz języki. Czym jest pop? w funkcji Add2Begin ma być cool i 2 = to, czy to drugie podejście do pisania funkcji?

Co do nazewnictwa, wezmę to sobie do serca. Funkcja add2begin jak sama nazwa wskazuje, jest do dodawanie elementu do listy na początek. Mam również funkcję dodawania na koniec listy oraz funkcję która dodaje elementy sortująco ( korzysta z dwóch funkcji wspomnianych obok ). Nie wstawiałem tu kodu, gdyż funkcje działają poprawnie. Wstawiłem Add2Begin aby pokazać ze funkcja do drukowania listy działa i nie sprawia problemów. Pop jest to element poprzedni, jak to w liście dwukierunkowej bywa ( tak mnie nauczono!) każdy element wie co stoi za nim i przed nim. U mnie to nazwy pop i nast. Dalszej części Pana pytania nie rozumiem.

kq napisał(a):

poczytaj o dziedziczeniu, bo to co robisz kompletnie nie ma sensu. Prywatnie dziedziczysz po Lista, grupa dziedziczy po Czytelniku. Wtf?

Dziedziczenie grupy po czytelniku faktycznie jest niepotrzebne. Dziękuję.

kq napisał(a):

nagie new i delete to antyidiomy w C++, używaj smart pointerów i kontenerów z biblioteki standardowej

Czy mógłby Pan jaśniej? Jak już wspomniałem jestem nowicjuszem, nie rozumiem za bardzo co ma Pan na myśli ze smart pointerami itp.

3

Bez przesady z panami ;)

@ nazewnictwo: pop jest często używane do zdejmowania ostatniego elementu ze stosu/tablicy ( http://en.cppreference.com/w/cpp/container/stack/pop http://en.cppreference.com/w/cpp/container/priority_queue/pop http://en.cppreference.com/w/cpp/container/queue/pop http://ruby-doc.org/core-2.2.0/Array.html#method-i-pop http://www.w3schools.com/jsref/jsref_pop.asp )

Jako rozwinięcie poprzedniego - faktycznie ma sens, ale jak widzisz takie mieszanie języków nie jest zbyt czytelne

Add2Begin - nie wiedziałem jak to interpretować, były dwie opcje:

  1. jesteś zbyt cool, żeby pisać AddToBegin
  2. miałeś już funkcję AddBegin, ale ona nie działa poprawnie, więc piszesz ją po raz drugi

@ dziedziczenie - a po co prywatnie dziedziczysz z Lista? Nawet jeśli faktycznie tego używasz to jest to prawdopodobnie zły design

@ smart pointery / kontenery:

  1. nie wymyślasz koła na nowo
  2. nie ryzykujesz błędów implementacyjnych (zapomniane delete, zapomniana ścieżka wykonania, np. jakaś niejawna - wyjątek itd)
  3. jasno deklarujesz swoje intencje (kto staje się właścicielem obiektu)
  4. polecam punkty od https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c149-use-unique_ptr-or-shared_ptr-to-avoid-forgetting-to-delete-objects-created-using-new w dół

Kontenery - podobnie jak wyżej. std::list zamiast tej listy by było czytelniejsze, pewniejsze i być może nawet wydajniejsze (nie wspominając o std::vector)

0
  1. Faktycznie mieszanie języków ma swoje minusy. Mam nauczkę.
  2. Osoba, która mnie uczy programowania ma takie nawyki i przez to Add2Begin itp weszło mi do głowy.
  3. Jak to prywatnie dziedzicze? Masz na myśli
class Czytelnik : public Ksiazka, Lista 

? Bez tego konstruktor Czytelnik(string imie, string nazwisko, string TYTUL) {Imie = imie; Nazwisko = nazwisko; tytul = TYTUL; };

, który jest używany w kodzie tu nie zamieszczonym mi nie zadziała. 

edit: Mój błąd. Dziedziczenie po książce jest potrzebne, po liście nie. Dziękuję za zwrócenie uwagi. Niestety nie rozwiązuje to mojego problemu.
4. Już rozumiem. Zalecasz mi używanie gotowych tworów patrz lista itp. Lecz problem w tym, że chce zrobić to od nowa. Ze stworzeniem listy, dodawaniem elementów itp nie mam problemów. Jeśli kod dotyczy samej listy to wszystko jest okej. Jeśli jest to coś w stylu mini projketu z 1 postu to już tutaj pojawiają się problemy których nie widzę. 

Ogólnie program się kompiluje, przyjmuje ode mnie pierwszego czytelnika, drukuje go, przyjmuje drugiego czytelnika i podczas jego drukowania, program przerywa działanie. Nie mam pojęcia dlaczego...
3
  1. publicznie dziedziczysz po Ksiazka i prywatnie po Lista

Bez dziedziczenia po Lista się nie kompiluje? Nie widzę dlaczego. Ale nachodzi mnie pytanie: czym jest tytuł czytelnika, i dlaczego dziedziczy on po książce - nie pytam wyłącznie pod względem "bo mi się nie kompiluje" - tylko jaki to ma sens? Czytelnik nie jest rozszerzeniem książki.

  1. Nie ma nic złego w napisaniu swojej wersji w ramach ćwiczeń, aby zrozumieć jak to działa. Ale potem używaj już gotowych wersji z bibliotek. NIH to nie jest nic dobrego, a wersje biblioteczne są dużo bardziej zrozumiałe dla innych programistów i kompilatorów.
0

Jak wspomniałem w poprzednim poście dziedziczenie Czytelnika po książce było potrzebne do jednego konstruktora, który służył mi do testowania innej funkcji. Dziękuję za zwrócenie uwagi na dziedziczenie po liście. Natomiast dalej nie wiem co jest główną przyczyną mego problemu. Czy mógłbyś przekopiować kod do kompilatora? Z góry dziękuję.

1

Nie uważacie, że klasa Czytelnik, która dziedziczy po klasie Książka jest trochę bez sensu? Czytelnik posiada książki, a nie "jest" książką. Agregacja będzie lepsza.

1

Próbowałeś chociaż odpalić to w debuggerze? Od razu widać, co jest problemem:
user image

Dodając drugi element nigdzie nie ustawiasz K.

0
kq napisał(a):

Próbowałeś chociaż odpalić to w debuggerze? Od razu widać, co jest problemem. Dodając drugi element nigdzie nie ustawiasz K.

Prosiłbym o wytłumaczenie jak odpalić debuggera. Korzystam z dev c++.

Co do ustawiania K.

 
void dodaj(grupa* G, Czytelnik* C)
{
    C->pobierz();
    if (G->ekipa == NULL) {
        G->ekipa = new Czytelnik[1];
        G->ilosc = 1;
        G->ekipa[0].Imie = C->Imie;
        G->ekipa[0].Nazwisko = C->Nazwisko;
        G->ekipa[0].K = new Lista();

        cout << "ok po pustej";
    }

    else {
        Czytelnik* newEkipa = new Czytelnik[G->ilosc + 1]; // nowa tablica, powolywanie do zycia

        for (int i = 0; i < G->ilosc; i++) {
            newEkipa[i].Imie = G->ekipa[i].Imie;
            newEkipa[i].Nazwisko = G->ekipa[i].Nazwisko;
            newEkipa[i].K = G->ekipa[i].K;

            // new Lista(G->ekipa[i].K)

            cout << "ok po przepisaniu";
        }
        G->ilosc++;

        newEkipa[G->ilosc - 1] = Czytelnik(C->Imie, C->Nazwisko, C->K);
        cout << "ok po ostatnim";

        delete[] G->ekipa;
        G->ekipa = newEkipa;
        cout << "ok po usunieciu";
    }
}

Czy linijka

 
newEkipa[G->ilosc - 1] = Czytelnik(C->Imie, C->Nazwisko, C->K);

Korzystająca z konstruktora

 
Czytelnik(string imie, string nazwisko, Lista *R){Imie = imie; Nazwisko = nazwisko; K=R; };

nie ma własnie za zadanie tego robić? Nie wiem jak rozwiązać to w inny sposób.

4

A gdzie ustawiasz C->K? Tutaj:

    Czytelnik()
    {
        Imie = "";
        Nazwisko = "";
        K = NULL;
    };

:)

Dev-cpp nie znam, ale nie przypominam sobie aby miał wbudowaną obsługę debuggera. Polecam jakieś IDE z tej dekady, np. Qt Creator, CLion czy nawet Code::Blocks.

0

kq dziękuję Ci bardzo za poświęcony czas i udzielenie wskazówek! Zamiast ustawić K na nowa listę to ja ją "nullowałem". Jesteś mistrzem! Niech CI Bozia w dzieciach wynagrodzi :)

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