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

Projekt na studia, problem z polimorfizmem

0 głosów
199 wizyt
pytanie zadane 26 grudnia 2018 w C i C++ przez daqeck Nowicjusz (200 p.)
edycja 26 grudnia 2018 przez Patrycjerz

Hej piszę projekt na studia i mam problem z metodą liczącą najbliższą odległość miedzy obiektem a elementami kontenera list i zwracającą najbliższy element listy.

Tu macie link do pobrania całego projektu: 
https://drive.google.com/open?id=1rqy7531-4EnP0-tKErI1gVR-yJFQD1on

Błędy wyrzuca metoda:

Pasterz Owca::FindNearestPasterz

o treściach:

std::unique_ptr<Owca,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': cannot convert argument 1 from 'Object *' to 'std::nullptr_t  //xmemory0    881 

missing type specifier - int assumed. Note: C++ does not support default-int  //owca.h    16    

syntax error: missing ',' before '*'  //owca.h    16

Chodzi w niej o to aby owca znalazła najbliższego pasterza z kontenera. Odległość liczona jest na bazie współrzędnych, które mogą być wielowymiarowe, dlatego przechowywane są w tablicy dynamicznej.

Klasy Pasterz i Owca dziedziczą po klasie abstrakcyjnej Object.

1 odpowiedź

0 głosów
odpowiedź 26 grudnia 2018 przez adrian17 Ekspert (306,660 p.)
wybrane 27 grudnia 2018 przez daqeck
 
Najlepsza

missing type specifier - int assumed. Note: C++ does not support default-int  //owca.h    16    

syntax error: missing ',' before '*'  //owca.h    16

Masz okrężne include'y - GrupaPasterzy.cpp include'uje GrupaPasterzy.h, który include'uje ObjectFactory.h, który include'uje Owca.h, który potrzebuje już zdefiniowanego ZoorganizowanaGrupaPasterzy - ale go jeszcze nie ma.

Tak naprawdę nie potrzebujesz tego include'a w Owca.h, wystarczy foward deklaracja klasy ZoorganizowanaGrupaPasterzy.

std::unique_ptr<Owca,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': cannot convert argument 1 from 'Object *' to 'std::nullptr_t  //xmemory0    881

Jak głębiej poklikasz, zobaczysz, że błąd jest w

Object* x = ObjectFactory::create(linia);
lista.emplace_back(x);
// `lista` to std::list<std::unique_ptr<Owca>> lista;

Nasuwa się pytanie: jeśli wiesz jako fakt, że `x` będzie Owcą, po co używasz generycznej fabryki tworzącej obiekt dowolnej podklasy Object?

Jeśli nie wiesz, powinieneś albo przechowywać Object w liście, albo sprawdzać w kodzie, że faktycznie `x` jest obiektem klasy `Owca` (używając dynamic_cast).

(boczne pytanie: czemu std::list, a nie std::vector?)

komentarz 26 grudnia 2018 przez daqeck Nowicjusz (200 p.)
edycja 27 grudnia 2018 przez daqeck

@adrian17,

A jaka jest różnica między tym includem, a forward deklaracją? Bo rozumiem, że wystarczy mu wtedy tylko że coś takiego istnieje.

Wiem, że ta generyczna fabryka to lekki przerost nad treścią, ale muszę w projekcie w jakiś sposób wykorzystać polimorfizm. Mam jeszcze pytanie, w którym miejscu musiałbym użyć tego dynamic_casta do porównania, po wrzuceniu do listy czy może przed liczeniem odległości? Za dynamic_casta może jakieś dodatkowe punkty podłapie przy ocenianiu :D

Listę jakoś tak wybrałem, bo na zajęciach coś podobnego przerabialiśmy, poza tym jakoś bardzo od vectora się nie różni. Racja vector w sumie prostszy, ale przynajmniej będę zmuszony użyć iteratorów, żeby pokazać, że je rozumiem.

komentarz 27 grudnia 2018 przez daqeck Nowicjusz (200 p.)
edycja 27 grudnia 2018 przez daqeck

@adrian17,

Wymyśliłem coś takiego:

StadoOwiec::StadoOwiec(const std::string& file_name)
{
    std::ifstream input(file_name);
    if (!input.is_open())
        std::cout << "Nie udalo sie otowrzyc pliku\n";
    else
    {
        std::string linia;
        int y = -1;
        if (y == -1)                                        //ustalenie wymiaru i dodanie pierwszej linijki do kontenera
        {
            getline(input, linia);
            int l = linia.length();
            for (int i = 0; i < l; i++)
            {
                if (linia[i] == ' ')
                    y++;
            }
            Object::setn(y);
            Owca* x = dynamic_cast<Owca*>(ObjectFactory::create(linia));
            lista.emplace_back(x);
            //delete x;
        }

        while (getline(input, linia))                        //dodanie reszty linijek do kontenera
        {
            Owca* x = dynamic_cast<Owca*>(ObjectFactory::create(linia));
            lista.emplace_back(x);
            //delete x;
        }
    }
    input.close();
}

Żeby przy dodawaniu do listy zamieniał Object* na Owca* i tak samo przy pasterzu oraz zmieniłem że FindNearestPasterz zwraca teraz wskaźnik na Pasterza. Teraz program się uruchamia :D

Program wywala przy konstruktorze głębokiej kopii Owca wywoływanym w ObjectFactory. Najpierw uruchamia konstruktor dla Owcy a potem nie wiadomo dlaczego od razu dla Pasterza. Wydaję mi się że to może być przez to że te includy są źle, ale jeśli usunę include ZoorganizowanaGrupaPasterzy.h z pliku Owca.h i dam forward deklaracje to wtedy metoda FindNearestPasterz nie może korzystać z kontenera i wyrzuca błąd. 

komentarz 27 grudnia 2018 przez adrian17 Ekspert (306,660 p.)

A jaka jest różnica między tym includem, a forward deklaracją? Bo rozumiem, że wystarczy mu wtedy tylko że coś takiego istnieje. 

include nie działa, bo owca.h próbuje include'ować plik, który include'uje owca.h - to nie ma sensu (include guardy/pragma once służą dokładnie do zabezpieczenia przed tym).

PS. Jeśli usunę include ZoorganizowanaGrupaPasterzy.h z pliku Owca.h i dam forward deklaracje to wtedy metoda FindNearestPasterz nie może korzystać z kontenera i wyrzuca błąd.

pokaż? Upewnij się też, że plik .cpp ma już odpowiednie include'y.

adoptator = dynamic_cast<Pasterz*>(&**it);

Err, ale kontener już przechowuje Pasterz, więc nie rozumiem czemu tam chcesz robić casta. Kompilator narzeka na dokładnie te linie, które pokazałem wyżej:

Object* x = ObjectFactory::create(linia);
lista.emplace_back(x);
// `lista` to std::list<std::unique_ptr<Owca>> lista;

 

komentarz 27 grudnia 2018 przez daqeck Nowicjusz (200 p.)
Dodałem też do Owca.cpp #include "ZoorganizowanaGrupaPasterzy.h" i teraz też się uruchamia, ale dalej konstruktor dla Owcy (new Owca((double*)tmp)) w ObjectFactory uruchamia też konstruktor kopiujący dla Pasterza i nw dlaczego.
komentarz 27 grudnia 2018 przez adrian17 Ekspert (306,660 p.)

Poważnie: to mi nic nie mówi.

Problem z okrężnym include'm rozwiązałem dokładnie tak jak Ci powiedziałem:

//#include "ZoorganizowanaGrupaPasterzy.h"
class ZoorganizowanaGrupaPasterzy;
class Owca : public Object
// ...

 

komentarz 27 grudnia 2018 przez daqeck Nowicjusz (200 p.)
Nw czy zauważyłeś, ale zedytowałem poprzednią wiadomość odnośnie dynamic_casta chciałem to zrobić zanim jeszcze mi odpiszesz, ale nie zdążyłem :P

Wydaje mi się że powinienem mieć mniej więcej co mi przekazałeś:

https://drive.google.com/open?id=1KWwlF60G9-0UxZBrcDR6BUAsYnYBUkWf

Chyba, że coś jeszcze dopisałem co zepsuło te konstruktory
komentarz 27 grudnia 2018 przez adrian17 Ekspert (306,660 p.)
No i OK, teraz powinno działać... z tym, że to wciąż brzydkie ideowo - używasz generyczną fabrykę, a zakładasz z góry, że wyjdzie obiekt konkretnej klasy. W dodatku nie sprawdzasz, czy dynamic_cast się powiódł.

Nie widzę tu też za bardzo polimorfizmu... faktycznie ta fabryka zwraca obiekty klasy bazowej, ale jak projekt ma "użyć polimorfizm", to zazwyczaj chodzi o metody wirtualne.

W każdym razie powinno działać.

(BTW, widać że masz tam repo gita - więc nie rozumiem, czemu wrzucasz kod na GDrive, a nie normalnie na hosting typu GitHub.)
komentarz 27 grudnia 2018 przez daqeck Nowicjusz (200 p.)

A masz jakiś pomysł dlaczego wysypuje w tym miejscu? Dziwne bo wcześniej tak nie miałem.

W jaki sposób sprawdza się czy dynamic_cast się powiódł? :D

No mam repo z gita natomiast należy ono do politechniki i jest ustawione na prywatne, a swojego nigdy nie zakładałem, dlatego na google wysyłam

komentarz 27 grudnia 2018 przez adrian17 Ekspert (306,660 p.)

W jaki sposób sprawdza się czy dynamic_cast się powiódł?

Jeśli się nie powiódł, zwraca nullptr.

A masz jakiś pomysł dlaczego wysypuje w tym miejscu? 

'other' jest nullem. Powód widać ładnie w callstacku:

Owca::Owca(double* other) : Object(other), wlasciciel(nullptr) {}

 

komentarz 27 grudnia 2018 przez daqeck Nowicjusz (200 p.)
Dobra już widzę że zdefiniowałem wlasciciela jako Pasterz a nie Pasterz* i dlatego wysypywało.

Reszta działa, dzięki wielkie @adrian17 :D

Podobne pytania

0 głosów
0 odpowiedzi 165 wizyt
pytanie zadane 20 września 2016 w C i C++ przez plkpiotr Stary wyjadacz (12,420 p.)
0 głosów
1 odpowiedź 215 wizyt
0 głosów
0 odpowiedzi 59 wizyt

86,483 zapytań

135,239 odpowiedzi

300,481 komentarzy

57,230 pasjonatów

Motyw:

Akcja Pajacyk

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

Oto dwie polecane książki warte uwagi. Pełną listę znajdziesz tutaj.

...