2012-08-10 18 views
5

Zobaczmy ten kod:Jak wywołać wirtualną funkcję klasy pochodnej przez klasy bazowej wskaźnik

class CBase 
{ 
public: 
    virtual vfunc() { cout << "CBase::vfunc()" << endl; } 
}; 

class CChild: public CBase 
{ 
public: 
    vfunc() { cout << "CChild::vfunc()" << endl; } 
}; 

int main() 
{ 
CBase *pBase = new CBase; 
((CChild*)pBase)->vfunc(); // !!! important 
delete pBase; 
return 0; 
} 

Wyjście jest:

CBase::vfunc() 

Ale chcę zobaczyć: CChild :: vfunc()

Jawny ((CChild *) pBase) rzuca, aby wpisać "CChild *". Dlaczego więc wywołać pochodną vfunc() Potrzebuję zastąpić "ważny" ciąg z: ((CChild *) pBase) -> CChild :: vfunc();

Odpowiedz

6

To nie jest, jak to działa - to jest:

CBase *pBase = new CChild; 
pBase->vfunc(); 

virtual wywołania funkcji są rozwiązane dynamicznie na wskaźniki & odniesienia (chyba wywołać metodę wyraźnie, jak ty). Co oznacza, że ​​nie ma znaczenia, co powiesz kompilatorowi o wskaźnik, będzie szukał metody w vftable. Który w twoim przypadku to vftable z CBase.

5

Nie możesz. *pBase to obiekt typu CBase. Nie można traktować go tak, jakby był to obiekt CChild, ponieważ nie jest obiektem CChild.

Użycie wskaźnika uzyskanego przez rzutowanie do CChild* powoduje, że program wykazuje niezdefiniowane zachowanie.

+1

Jak więc rozumieć Schildt H .: "wskaźnik klasy bazowej może być również używany jako wskaźnik do obiektu dowolnej klasy wywodzącej się z tej bazy". – LEXX

+0

A więc dlaczego łańcuch ((CChild *) pBase) -> CChild :: vfunc() działa? – LEXX

+0

@LEXX: Po pierwsze, powinieneś nagrać tę książkę i dostać [dobrą wstępną książkę] (http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). To, co mówi w tym cytacie, jest poprawne. Wskaźnik klasy bazowej ('CBase *') może wskazywać na obiekt typu pochodzącego z 'CBase' (jak' CChild').Jednak to nie jest to, co zrobiłeś: wskaźnik twojej klasy bazowej wskazuje na obiekt klasy bazowej. Gdybyś miał na przykład 'CBase * p = new CChild();', wówczas 'p' byłby wskaźnikiem klasy bazowej wskazującym na obiekt klasy pochodnej. –

2

pozostałe odpowiedzi stanowią ważne punkty - uzupełnienie: jeśli w rzeczywistości masz do czynienia z CChild (np. Jest to parametr przekazany jako parametr), możesz użyć dynamic_cast do downcasta. jednak duże uzależnienie od dynamic_cast jest często wskazówką, że Twój projekt się nie udał.

szczegółów w obsadzie można znaleźć tutaj: http://msdn.microsoft.com/en-us/library/cby9kycs(v=vs.80).aspx

więc proces wiązałoby odlewanie parametr CChildCBase poprzez dynamic_cast, jeśli odwołanie jest CChild i dynamic_cast powiedzie, wówczas można mieć pewność, że są do czynienia z CChild, a następnie można bezpiecznie używać go jako CChild.

0

Problem tutaj wydaje się bardzo prosty. CBase nie może w magiczny sposób przejść na CChild! Pozwól mi przerobić twój przykład i dodać kilka komentarzy. Powinno być oczywista ...

#include <iostream> 

class CBase { 
public: 
    virtual void vfunc() { std::cout << "CBase::vfunc()" << std::endl; } 
    virtual ~CBase(){} // Virtual destructor... extremely important! I'll let you figure out why as an excercise 
}; 

class CChild: public CBase { 
public: 
    void vfunc() { std::cout << "CChild::vfunc()" << std::endl; } 
    ~CChild(){} // Is this destructor called? When? Try adding some std::cout to each destructor 
}; 

int main() 
{ 
    CBase *ptr1 = new CBase; 
    CBase *ptr2 = new CChild; 

    ptr1->vfunc(); // ptr1 points to an instance of CBase. This is what's important!! 
    ptr2->vfunc(); // ptr2 points to an instance of CChild, which can be referenced to as a CBase 

    delete ptr1; 
    delete ptr2; 
} 

wyjściowa:

CBase::vfunc() 
CChild::vfunc() 

PS: Właśnie uświadomiłem sobie, że jestem około 5 lat późno do partii, ale ponieważ uważam wartość edukacyjną na tym I” Napisz to mimo wszystko!

Powiązane problemy