• 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.

Aruba Cloud VPS - 50% taniej przez 3 miesiące!
0 głosów
1,150 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ź 781 wizyt
0 głosów
1 odpowiedź 180 wizyt
0 głosów
1 odpowiedź 592 wizyt

93,187 zapytań

142,201 odpowiedzi

322,012 komentarzy

62,514 pasjonatów

Advent of Code 2024

Top 15 użytkowników

  1. 2127p. - dia-Chann
  2. 2092p. - Łukasz Piwowar
  3. 2079p. - Łukasz Eckert
  4. 2037p. - Tomasz Bielak
  5. 2006p. - Michal Drewniak
  6. 2006p. - rucin93
  7. 2005p. - Łukasz Siedlecki
  8. 1964p. - CC PL
  9. 1946p. - Adrian Wieprzkowicz
  10. 1901p. - Mikbac
  11. 1744p. - rafalszastok
  12. 1734p. - Anonim 3619784
  13. 1586p. - Dawid128
  14. 1520p. - Marcin Putra
  15. 1480p. - ssynowiec
Szczegóły i pełne wyniki

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

Wprowadzenie do ITsec, tom 1 Wprowadzenie do ITsec, tom 2

Można już zamawiać dwa tomy książek o ITsec pt. "Wprowadzenie do bezpieczeństwa IT" - mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy aż 15% zniżki! Dziękujemy ekipie Sekuraka za fajny rabat dla naszej Społeczności!

...