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

Dziedziczenie a dostęp do zmiennych w klasach bazowych i pochodnych.

Object Storage Arubacloud
0 głosów
910 wizyt
pytanie zadane 4 lipca 2018 w C i C++ przez Zwykły śmiertelnik Nowicjusz (160 p.)
edycja 4 lipca 2018 przez Zwykły śmiertelnik

Witam,

Już od jakiegoś czasu uczę się C++ i w tej chwili próbuję jak najlepiej zrozumieć pojęcia związane z dziedziczeniem i polimorfizmem. W ramach ćwiczeń postanowiłem stworzyć prosty program. W programie tym jest klasa bazowa i jej podklasy. W każdej klasie (i w bazowej, i w pochodnych) jest zmienna int o tej samej nazwie ("value"). W każdej klasie napisałem konstruktor, który nadaje zmiennej "value" różną wartość w zależności od  klasy - w klasie bazowej konstruktor nadaje "value" wartość 0, a w innych klasach wartościami tymi są różne dodatnie liczby. Z ciekawości chciałem sprawdzić jak da się wyświetlać zmienne w tych klasach za pośrednictwem wskaźników. Zakładając, że klasa bazowa ( z której inne klasy dziedziczą) nazywa się "Bazowa" , a jedna z klas pochodnych nazywa się "Podklasa": 

class Bazowa
{public:
int value;
Bazowa(int w=0)
{value=w;}};

class Podklasa:public Bazowa
{public:
int value;
Podklasa(int w=2)
{value=w;}};

Następnie utworzyłem reprezentanta Podklasy o nazwie "subclass" i  wskaźnik Bazowa *wsk1 i próbowałem wyświetlić zmienną value (równą 2) z subclass.

Podklasa subclass;
Bazowa *wsk1;
wsk1=&subclass;

cout<<wsk1->value<<endl; /*wyswietla 0, czyli value nadawane przez konstruktor klasie bazowej, a nie value nadawane przez konstruktor klasie pochodnej Podklasa*/

Zauważyłem, że przy innym skonstruowaniu wskaźnika, da się tę wartość wyświetlić:

Podklasa subclass;
Podklasa *wsk1;
wsk1=&subclass;

cout<<wsk1->value<<endl; /*wyswietla 2, czyli value nadawane przez konstruktor klasie pochodnej, a nie value nadawane przez konstruktor klasie bazowej*/

Nie rozumiem dlaczego tak się dzieje i czy jest jakiś sposób, aby za pomocą wskaźnika Bazowa *wsk1 uzyskać dostęp do int value u reprezentanta Podklasy (klasa pochodna klasy Bazowa) o nazwie "subclass" ?

Pytanie chyba dla większości osób tutaj, może być banalnie proste, ale ja niestety nie mogę znaleźć klarownego wyjaśnienia (albo czytam i zupełnie nie rozumiem). Czy ma to jakiś związek z wiązaniem dynamicznym i statycznym ? Czy jest to gdzieś łopatologicznie wyjaśnione ? Generalnie chodzi mi o temat dostępu do klas pochodnych do bazowych i na odwrót oraz z klas pochodnych do innych klas pochodnych za pomocą wskaźników i nie tylko. Z góry dziękuję za pomoc.

Pozdrawiam  

komentarz 4 lipca 2018 przez Hiskiel Pasjonat (22,830 p.)
A gdzie inicjalizujesz tą wartość?

Poza tym, lepiej użyć listy inicjalizacyjnej.
komentarz 4 lipca 2018 przez Zwykły śmiertelnik Nowicjusz (160 p.)
Czy konieczna jest inicjalizacja ? Nie osiągnie się tego samego poprzez deklarację i potem przypisanie wartości za pomocą konstruktora ? Czy inaczej deklaruje się zmienne wewnątrz klasy niż np. int value=0; ?

Czy użycie listy inicjalizacyjnej w tym przypadku dużo by zmieniło ?
komentarz 4 lipca 2018 przez Hiskiel Pasjonat (22,830 p.)
Dobra, nie zauważyłem, że int w ma wartość domyślną. W takim razie nie wiem o co może chodzić. Poczekaj na kogoś innego. Ludzie tutaj naprawdę dobrze udzielają pomocy.

 

A apropos listy:

https://www.p-programowanie.pl/cpp/lista-inicjalizacyjna/

3 odpowiedzi

+1 głos
odpowiedź 4 lipca 2018 przez LislaV Bywalec (2,400 p.)

Mimo, że do wsk1 przypisujesz adres obiektu klasy pochodnej, to zmienna wsk1 jest wciąż wskaźnikiem na obiekt klasy bazowej. Zatem, używając wsk1 masz dostęp tylko do zmiennych i funkcji klasy bazowej.

Aby mieć dostęp do danych do klasy pochodnej używając wskaźnika na klasę bazową potrzebujesz funkcji wirtualnych. Przykład:

#include <iostream>

using namespace std;

class Bazowa
{
public:
    int value;
    Bazowa(int w = 0)
    {
        value = w;
    }

    virtual int getValue() const
    {
        return value;
    }
};

class Podklasa : public Bazowa
{
public:
    int value;

    Podklasa(int w = 2)
    {
        value = w;
    }

    int getValue() const
    {
        return value;
    }
};

int main()
{
    Podklasa subclass;
    Bazowa* wsk1;
    wsk1 = &subclass;

    cout << wsk1->getValue() << endl;

    return 0;
}

Teraz, wywołując metodę getValue() kompilator najpierw szuka tej metody w klasie bazowej, ale przez to, że ta metoda jest wirtualna, szuka przeciążonej funkcji w klasie pochodnej i dopiero teraz zostanie zwrócona wartość 2. Gdyby usunąć słówko "virtual", zostałaby wywołana metoda getValue() z klasy bazowej i zwrócona by została wartość 0 smiley

0 głosów
odpowiedź 4 lipca 2018 przez 99xkubax99 Obywatel (1,780 p.)

Witaj,

Jak już kolega powyżej wspomniał, używając wskaźnika na obiekt klasy bazowej i przypisując do niego obiekt klasy pochodnej, mamy dostęp tylko do zmiennych i funkcji klasy bazowej. Wiem, że podany przez Ciebie przykład jest prostą ilustracją problemu, jednak zawsze o wiele lepiej jest używać do deklarowania składowych klasy niebędących metodami klauzuli private oraz publicznych tzw getterów aby tą wartość pobrać. Gdybyś jednak chciał zmodyfikować podany przez Ciebie przykład, nie używając tych dobrodziejstw, mógłbyś poprawić kod, aby konstruktor klasy pochodnej (Podklasa(int w=2) inicjalizował listą obiekt klasy bazowej. Wtedy co prawda wartość zmiennej z klasy bazowej ulega zmianie, ale wykonując wtedy polecenie:

cout << wsk->value << endl;

zostanie wyświetlana wartość składowej z klasy bazowej, która została zainicjalizowana wartością podaną w konstruktorze klasy pochodnej. Konstruktor klasy pochodnej wyglądałby wtedy tak: 

Podklasa(int w = 2): Bazowa(w)
	{
		value = w;
	}

Jest to jakieś wyjście z tej sytuacji, ale jednak kiedy tylko możliwe, lepiej używać ograniczania dostępu do składowych klas oraz wirtualnych funkcji zwracających wartość skłądowych.

0 głosów
odpowiedź 4 lipca 2018 przez Zwykły śmiertelnik Nowicjusz (160 p.)
Dziękuję Wam za wyczerpujące odpowiedzi. Teraz wiem, że są różnice między tym jak zachowują się zmienne a jak metody w obrębie takiego "łańcucha" klas zaczynającego się od klasy bazowej. Na innym forum w odpowiedzi na taki sam post zasugerowano mi rzutowanie w dół, czego wykonanie nie do końca rozumiem. Czy na tym etapie warto już się tym zainteresować ? Czy raczej w większości przypadków są stosowane właśnie tzw. gettery ?
komentarz 4 lipca 2018 przez 99xkubax99 Obywatel (1,780 p.)
Rzutowanie w dół ma miejsce kiedy do obiektu klasy bazowej przypisujesz obiekt klasy pochodnej, lub jeżeli hierarchia jest bardziej "rozbudowana", do obiektu klasy pochodnej przypisujesz obiekt klasy pochodnej, który z niej dziedziczy. Nazwa w dół wzięła się stąd, że wskutek dziedziczenie publicznego, obiekt klasy pochodnej "jest" obiektem klasy bazowej, może także zawierać dodatkowe unikatowe "cechy". Tak więc dla przykładu jeżeli mamy klasę Owoc, który posiada składową masa, oraz klasę pochodną Banan dziedziczącą publicznie z klasy bazowej Owoc i dodatkowo zawierającą składową długość, to możemy jawnie bądź niejawnie rzutować w dół obiekt klasy Banan na obiekt klasy Owoc z tego względu, że Banan jest owocem, ale zostanie wtedy pozbawiony swojej składowej długość (dlatego w dół), więc przez to traktujemy obiekt klasy pochodnej jako obiekt klasy bazowej. Jest jeszcze rzutowanie w górę, ale nie jest ono domyślne, ponieważ jak pewnie zauważyłeś, Owoc nie zawsze musi być bananem. W podanym przez Ciebie przypadku jednak nie uważam, aby rzutowanie w dół było lepszym rozwiązaniem, lepiej zostać przy wspomnianych sposobach.

Podobne pytania

0 głosów
1 odpowiedź 510 wizyt
0 głosów
1 odpowiedź 151 wizyt
0 głosów
1 odpowiedź 480 wizyt

92,555 zapytań

141,403 odpowiedzi

319,557 komentarzy

61,940 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

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy 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!

...