• 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

Cloud VPS
0 głosów
2,473 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,570 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 (158,840 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 (158,840 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 (158,840 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,570 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ź 298 wizyt
0 głosów
1 odpowiedź 304 wizyt

93,469 zapytań

142,404 odpowiedzi

322,713 komentarzy

62,852 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

Kursy INF.02 i INF.03
...