Definicja zwrotna a dziedziczenie

0

Witam,

Napisałem poniższy kod oparty na kompozycji:

#include <iostream>

using namespace std;

class Osoba;

class Nauczyciel {
public:
    Osoba *osoba;
    double pensja;
};

class Osoba {
public:
    string nazwisko;
    int pesel;
};

int main(void) {
    Nauczyciel n1;

    n1.osoba = new Osoba();
    n1.osoba->nazwisko = "Kowalski";
    n1.pensja = 2000.0;

    cout << n1.osoba->nazwisko << " " << n1.pensja << endl;

    delete n1.osoba;

    return 0;
}

Jak widać, w związku z tym że hierarchia klas została naruszona, użyłem definicji zwrotnej (prototypu) klasy Osoba, by wstępnie poinformować kompilator o tym, że "gdzieś w tym pliku ona jest".

Chciałbym z tego samego mechanizmu skorzystać przy dziedziczeniu. Kierując się analogią napisałem następujący kod:

#include <iostream>

using namespace std;

class Osoba;

class Nauczyciel : public Osoba {
public:
    double pensja;
};

class Osoba {
public:
    string nazwisko;
    int pesel;
};

int main(void) {
    Nauczyciel *n1 = new Nauczyciel;

    n1->nazwisko = "Nowak";
    n1->pensja = 2000.0;
    
    cout << n1->nazwisko << " " << n1->pensja << endl;
    
    delete n1;

    return 0;
}

Oczywiście g++ nie może tego skompilować. Zastanawiam się dlaczego? W jaki sposób mogę to rozwiązać?

Z góry dziękuję za zainteresowanie,
M.

0

Nie możesz, pełna definicja klasy Osoba musi być znana przed definicją klasy Nauczyciel. Zamiana klas miejscami pomoże ;)

0

Witaj,

Dziękuję za odpowiedź.

Zamiana miejsc... to właśnie od tej strony wyszedłem :). Skoro przy kompozycji jest to możliwe, dlaczego przy dziedziczeniu już nie?

Btw. Miałem na myśli deklarację zwrotną, nie definicję.

0

Dlaczego sie nie da? Zastanów się co kompilatorowi jest potrzebne :)
Jeśli polem klasy jest wskaźnik do jakiegoś typu, to zasadniczo kompilator musi wiedzieć tylko że ten typ istnieje (bo wskaźnik na cokolwiek ma zawsze taki sam rozmiar, więc kompilator bez problemu może obiekt takiej klasy utworzyć, bo wiadomo jak będzie skonstruowany). Jeśli chcesz dziedziczyc to pojawia się problem -> kompilator musi wiedzieć jaki jest rozmiar obiektu klasy z której dziedziczysz i jakie są jej pola, bo bez tego nie byłby w stanie utworzyć obiektu tej klasy. Sama informacja o tym ze taki typ istnieje to za mało.

0

@Shalom
Witaj,

W takim razie jestem "skazany" na konstruktor i listę inicjalizacyjną czy rozbicie tego na dwa pliki (w tym jeden *.h)?

Rozumiem sam problem oraz zapotrzebowanie kompilatora. Zastanawiam się tylko, w jaki sposób dać mu to czego potrzebuje w przypadku dziedziczenia.

0

A gdzie widzisz problem? Bo nie bardzo rozumiem. Przecież w twoim przykładzie wystarczy zmiana kolejności? Jak masz najbardziej ekstremalny problem -> krzyżowe referencje, to rozbicie na pliki nagłówkowe i pliki z kodem, oraz zastosowanie strażników nagłówka spokojnie załatwia sprawę.

0

Żeby sobie takich problemów oszczędzić możesz od razu rozbić to na pliki nagłówka i implementacji. W razie czego wrzucasz sobie forward declaration i wsio pomyka.
Prosta zasada, jeżeli używasz w kodzie czegokolwiek poza samą nazwą klasy (czyli np jakiejś metody) to już musisz mieć kompletny typ (tj klasa z deklaracjami, implementacji oczywiście jeszcze kompilator znac nie musi, bo i po co). Jeżeli nie, to wystarczy sama deklaracja klasy.

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