Dziedziczenie- dostanie sie do atrybuty klasy pochodnej

0

Taki wyświechtany przykład. Mam klasę bazową Samochód i klasę pochodną Maluch. Klasa samochód posiada atrybut powiedzmy int waga, a klasa pochodna dodatkowo int ilosc_wypadkow. Teraz tworze wskaźnik samochodu i przypisuje obszar pamięci odpowiadający Maluchowi Samochód*=new Maluch. Teraz chce dostać się przez ten wskaźnik do atrybutu malucha Samochod->ilosc_wypadkow, ale kompilator wywala błąd, że nie ma takiej zmiennej w klasie Samochod. Co trzeba zrobić, żeby taki coś zadziałało? Odpowiedź typu: " zrób sobie od razu obiekt Maluch" mnie za bardzo nie interesuje. W kodzie wyglądałoby to mniej więcej tak:

class CSamochod
{
public:
	CSamochod(void);
	~CSamochod(void);
int waga;
}


class CMaluch :
	public CSamochod
{
public:
int ilosc_wypadkow;
}

void main()
{
CSamochod* ptr;
ptr=new CMaluch;
ptr->ilosc_wypadkow=5; //blad
}

 
1

Lekcja na dziś: polimorfizm. Przypisanie klasy pochodnej do wskaźnika na klase bazową przydaje się kiedy masz pewne abstrakcyjne USŁUGI które są realizowane w różny sposób w pochodnych obiektach. W przykładzie który pytałeś to nie działa. Poza tym dostęp do składowych NIE jest polimorficzne. W skrócie: nie da sie tego tak zrobić.
Zresztą czemu tylko maluch ma ilość wypadków? Inne samochody nie mogą mieć? o_O Przykład jak wygląda polimorfizm:

#include <iostream>
using namespace std;


class Samochod
{
public:
  virtual void otworzKlape(){ //ta funkcja moglaby być czysto wirtualna
    cout<<"Jakis samochod otwiera klape"; 
  }
};
 
 
class Maluch : public Samochod
{
public:
  virtual void otworzKlape(){
    cout<<"Maluch otwiera klape z tylu, bo tam ma silnik"<<endl;
  }
};

class DuzyFiat : public Samochod
{
public:
  virtual void otworzKlape(){
    cout<<"Duzy fiat otwiera klape z przodu, bo tam ma silnik"<<endl;
  }
};
 
int main()
{
Samochod* ptr1;
Samochod* ptr2;
ptr1=new Maluch();
ptr2=new DuzyFiat();
ptr1->otworzKlape();
ptr2->otworzKlape();
return 0;
}

To co ty chcesz zrobić jest nielogiczne. Bo SAMOCHÓD nie ma takiego pola. To tak samo jakbyś miał pewną usługę która jest specyficzna dla konkretnego modelu samochodu. Nie możesz takiej metody wywołać na rzecz samochodu, bo dla samochodu w ogólności nie ma ona sensu.

0

Już pomińmy logikę mojego przykładu. Chodzi mi o sam problem i celowo go tak uprościłem. Takie rozwiązanie kazał mi zastosować mój prowadzący laboratoria przy okazji realizacji projektu na zaliczenie(automat wydający napoje, ciastka, batony itp), więc coś chyba musi w tym być i na pewno chodzi tu o polimorfizm z którego nie wiem jak w tym przypadku skorzystać. W oryginale wygląda to tak, że mam klasę bazową Produkty i dziedziczące po niej klasy Slodycze i Napoje. Nie posiadają one żadnych metod, tylko atrybuty i akcesory do nich. I teraz problem, który przedstawiłem wcześniej w uproszczonej wersji. Mam mieć tablicę wskaźników typu Produkty* (póki co statyczną) przechowującą wszystko co jest aktualnie w zasobniku (produkty typu Slodycze i Napoje). Klasa napoje ma 2 atrybuty dodatkowe, do których właśnie nie mogę się dostać przez tą tablice typu Produkty*. I w sumie jestem w kropce, bo program mam już zrobiony deklarując każdy produkt osobno. Prowadzący tłumaczył się tym, że moje rozwiązanie blokuje dalszy rozwój programu, a tablice zawsze można zwiększyć. Teraz trochę kodu, konstruktor z mojego starego rozwiązania(wykomentarzowane) + nowe niedziałające rozwiązanie z tablicą (pominę akcesory):




public:
	Czasobnik(void);
	~Czasobnik(void);

private:

	Cprodukt* produkty[5]; 
/*
	Cslodycze* marsy;         //stare rozwiazanie
	Cslodycze* snikersy;
	Cnapoje* tigery;
	Cnapoje* cole;*/

konstruktor

Czasobnik::Czasobnik(void)
{
/*
	marsy=new Cslodycze("Mars", 2, 0,1);          //stare rozwiazanie
	snikersy=new Cslodycze("Snickers", 2, 0,2);
	tigery=new Cnapoje("Tiger","Puszka", 4, 0, 0.33,3);
	cole=new Cnapoje("Coca cola", "Butelka", 2, 0, 0.5,4);*/

	produkty[0]=new Cslodycze("Mars", 2, 0,1);
	produkty[1]=new Cslodycze("Snickers", 2, 0,2);
	produkty[2]=new Cnapoje("Tiger","Puszka", 4, 0, 0.33,3);       // dodatkowo atrybuty z klasy napoje, do ktorych nie ma dostepu(pojemnosc i opakowanie)
	produkty[3]=new Cnapoje("Coca cola", "Butelka", 2, 0, 0.5,4);
	
}

produkty[3]->GivePojemnosc()  //blad

1

Rozwiazania są dwa:

  1. Metoda getAdditionalParameters() która w zalezności od produktu potrafi zwrócić jakieś dodatkowe informacje
  2. Metoda getParamter(string parameterName) która sprawdza czy taki parametr istnieje i jeśli tak to go zwraca. Wymaga to przechowywania w produkcie atrybutów za pomocą mapy map<string,value>
0

Sprawę rozwiąząłem tak, że dodałem do klasy bazowej 2 czysto wirtualne akcesory(bez definicji) do tych dodatkowych atrybutów. W klasie pochodnej są takie same(o tej samej nazwie), zwykłe akcesory z normalna definicją. I śmiga. Pozdro.

1

No dobra, ale co w takim razie zwracają obiekty które tych dodatkowych atrybutów nie mają? Rzucają chociaż jakiś wyjątek? Bo programowanie to nie jest kwestia tego "czy działa czy nie", tylko jak działa i czy jest zgodne z jakąś logiką...

0

Generalnie masz racje. Dopisałem do definicji akcesorów virtualnych wyjątek o braku odpowiednich danych. Wydaje mi się, że takie rozwiązanie jest całkiem dobre i logiczne. Chociaż nawet bez tego wyjątku program może sprawnie funkcjonować i być dalej rozwijany. Jest potencjalne ryzyko wywołania akcesora dla obiektu który nie ma danego atrybutu, ale to już chyba zmartwienie programisty (czyli mnie) żeby czegoś takiego nie robić :] . Dzięki za pomoc.

0

Rzucenie wyjątku to jest taka próba ratowania sytuacji, a nie dobre rozwiązanie ;) Gdybyś nic nie rzucał to by była w ogóle jakaś tragedia. Na przyszłość radzę przemyśleć design zanim zaczniesz pisać ;)

0

Kolega pyta jak się dostać do pola specyficznego dla klasy pochodnej, proszę bardzo:
dynamic_cast<Maluch*>(Samochód)->ilosc_wypadków

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