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

Odwołanie się do metod klasy pochodnej przez wskaźnik na typ klasy bazowej

Object Storage Arubacloud
0 głosów
1,914 wizyt
pytanie zadane 12 września 2017 w C i C++ przez Dregon Początkujący (250 p.)

Cześć! Jestem samoukiem i uczę się c++ od paru miesięcy także przepraszam jeśli gdzieś używam złego nazewnictwa :) Potrzebuję porady odnośnie jednej rzeczy, więc nie przedłużając:

Mam abstrakcyjną klasę bazową A i powiedzmy parę klas pochodnych od niej, B, C i D. Tworzę tablicę wskaźników na typ klasy bazowej A. Do wskaźnika tworzę operatorem new obiekt B. Następnie za pośrednictwem wskaźnika na tym klasy bazowej A, chce się odwołać do metody w klasie B, która jest zadeklarowana tylko w tej klasie. Niestety podczas kompilacji wyskakuje komunikat, że klasa A "has no member named...", co poniekąd mnie nie dziwi. I teraz moje pytanie, czy jest jakoś możliwe odwołanie się do tej metody poprzez ten wskaźnik lub może inaczej? Do nowo utworzonego obiektu B mamy dostęp tylko przez wskaźnik ze względu na anonimowość obiektu B?

class A {...};
class B : public A {...};
A *przyklad[5];
przyklad[0] = new B;
przyklad[0]->metodaZadeklarowanaTylkoWKlasieB();

Wpadłem na pomysł aby po prostu napisać funkcję wirtualną w klasie A, a następnie w klasie pochodnej B ponownie ją zdefiniować. Ale czy to rozwiązanie będzie eleganckie? Ponieważ w klasie C i D pochodnej też od A, nie potrzebuję takiej funkcji, wyłącznie jest mi potrzebna w klasie B. To co wtedy zrobić z nią w klasach C i D? A może pokombinować jakoś z "przyjaźnią"?

Pozdrawiam i z góry dziękuję za pomoc :)

5 odpowiedzi

0 głosów
odpowiedź 12 września 2017 przez MetGang Nałogowiec (34,360 p.)

Niestety jedynym rozwiązaniem jest stworzenie wirtualnej (lub też nie) bazy funkcji w klasie A i redefinicja jej w klasie B. Tak działa polimorfizm. Możesz jej nie używać w klasach C i D, ale musi być ona w klasie A jeśli jakakolwiek klasa z niej dziedzicząca ma mieć możliwość jej wykonania.

Ewentualnie możesz zrobić tak:

static_cast<B*>(przyklad[0])->metodaZadeklarowanaTylkoWKlasieB();

lecz takie rozwiązanie mija się z celem i wymaga nośnika informacji, że do A* zostało przypisane new B, a to zalatuje starymi interfejsami z języka C. dynamic_cast poniekąd jest rozwiązaniem, ale lepszy jednak jest polimorfizm.

komentarz 12 września 2017 przez criss Mędrzec (172,590 p.)
Chyba masz literówke w tym fragmencie kodu - tzn. mówisz o dynamic_cast, a w kodzie jest static_cast :P
komentarz 12 września 2017 przez Knayder Nałogowiec (37,640 p.)
Lepiej tutaj użyć dynamic_casta, bo jeżeli nie uda się przerzutować, to po prostu zwróci nam NULL.
komentarz 12 września 2017 przez MetGang Nałogowiec (34,360 p.)
Być może źle rozdzieliłem wypowiedź i przez to nie jest ona jasna. Zaproponowałem rozwiązanie ze static_castem jeśli mamy pewność z jakim wskaźnikiem obcujemy (wtedy jest to zwykłe, czyste rzutowanie wskaźnika). Natomiast dynamic_cast dodałem jako dodatkowe rozwiązanie, które według mnie nie jest zbyt eleganckie głównie poprzez potrzebę walidacji wskaźnika i dodatkową robotę kompilatora. Polimorfizm robi to za nas i to w lepszym stylu :p
1
komentarz 12 września 2017 przez mokrowski Mędrzec (155,460 p.)
@MetGang a wiesz co kompilator robi by uzyskać polimorfizm a co gdy rzutuje dynamic?
komentarz 12 września 2017 przez MetGang Nałogowiec (34,360 p.)

dodatkową robotę kompilatora

Miałem na myśli static_cast i dynamic_cast.

Polimorfizm robi to za nas i to w lepszym stylu :p

Lepszy styl w sensie nie musimy ręcznie sprawdzać czy wskaźnik jest zdatny do użycia.

A odpowiadając bezpośrednio na pytanie: Nie wiem.

Osobiście nigdy nie potrzebowałem użyć dynamic_casta, jak i też bardzo rzadko widzę jego zastosowanie.

komentarz 12 września 2017 przez mokrowski Mędrzec (155,460 p.)
@MetGang nie ma "jedynych rozwiązań". Są rozwiązania które mają koszt, zalety i efekty uboczne. Ucząc się obsługi młotka, nie popełniaj błędu traktowania świata jako jeden wielki gwóźdź :-)
komentarz 12 września 2017 przez MetGang Nałogowiec (34,360 p.)
Możliwe, że się zapędziłem z tym stwierdzeniem. Zwłaszcza, gdy młotek przypomina buzdygan :p
0 głosów
odpowiedź 12 września 2017 przez mokrowski Mędrzec (155,460 p.)
edycja 12 września 2017 przez mokrowski
#include <iostream>

class A {
public:
    virtual ~A() {}
};

class B: public A {
public:
    void info() const {
        std::cout << "A info() method\n";
    }
};

int main() {
    A * aPtr = new B();
    B * bPtr = dynamic_cast<B *>(aPtr);
    bPtr->info();
    delete aPtr;
}

A informacje czy jest to klasa B, uzyskasz badając wskaźnik. W przypadku nieudanego rzutowania zwraca nullptr.

PS. Np. tak:

#include <iostream>
#include <vector>

class A {
public:
    virtual ~A() {}
};

class B: public A {
public:
    void info() const {
        std::cout << "A info() method\n";
    }
};

class C: public A {
};

int main() {
    std::vector<A *> ptrAVector{ new C(), new C(), new B(), new B()  };

    B * ptrB;
    for(auto& ptr: ptrAVector ) {
        if((ptrB = dynamic_cast<B *>(ptr))) {
            ptrB->info();
        }
    }

    for(auto& ptr: ptrAVector) {
        delete ptr;
    }
}

 

0 głosów
odpowiedź 12 września 2017 przez Knayder Nałogowiec (37,640 p.)

Możesz, jednak nie jest to dobrze zrobione.
Wszystko powinno wynikać z polimorfizmu.
Jeżeli w klasie A, masz jakąś metodę, która nie jest potrzebna w klasie B i C (Dziedziczą z klasy A), to znaczy że źle to zrobiłeś i musisz to inaczej wykminić.

A tym sposobem jest dynamic_cast (Nie static_cast jak wspominał MetGang)
 

#include <iostream>


class A {
public:
	int value;
	A() : value(0) {}
	virtual void funcA() {
		std::cout << "Im function in class A!\n";
	}
};

class B : public A {
public:
	void funcB() {
		std::cout << "Im only in class B!: " << value << '\n';
	}
};

int main()
{

	A* obj = new B;
	obj->value = 12345;

	std::cout << "Start value: " << obj->value << '\n';

	B* dynamicObj = dynamic_cast<B*>(obj);
	dynamicObj->funcB();
	dynamicObj->value = 54321;

	std::cout << "Same object value: " << obj->value << '\n';

	std::cin.get();

	delete obj;
	return 0;
}
Start value: 12345
Im only in class B!: 12345
Same object value: 54321

 

0 głosów
odpowiedź 12 września 2017 przez criss Mędrzec (172,590 p.)

Wpadłem na pomysł aby po prostu napisać funkcję wirtualną w klasie A, a następnie w klasie pochodnej B ponownie ją zdefiniować.

Jak najbardziej. Do tego służy polimorfizm.

 Ale czy to rozwiązanie będzie eleganckie? Ponieważ w klasie C i D pochodnej też od A, nie potrzebuję takiej funkcji, wyłącznie jest mi potrzebna w klasie B.

To może oznaczać, że B nie powinno czy nie musi dziedziczyć z A . Możesz też rozważyć rozdzielenie A na dwie klasy: A1 (zawierającą tą nieszczęsną metodę)  i A2. W tej sytuacji B dziedziczy zarówno z A1 jak i A2, a C i D tylko z A2. C++ oferuje wielokrotne dziedziczenie - nie bójmy się z tego korzystać :)

0 głosów
odpowiedź 12 września 2017 przez Dregon Początkujący (250 p.)
Dziękuję bardzo za wszystkie odpowiedzi, a w ramach nauki rozwiąże ten problem metodą z dynamic_cast, jak i wielodziedziczeniem :)

Podobne pytania

0 głosów
1 odpowiedź 237 wizyt
0 głosów
1 odpowiedź 192 wizyt

92,549 zapytań

141,392 odpowiedzi

319,517 komentarzy

61,933 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!

...