2012-04-02 10 views
10

Tak, wiem, że downcast przy użyciu dynamic_cast nie może się skompilować, jeśli Base nie jest polimorficzny, ale mój problem nie dotyczy tego.`dynamic_cast` from Base to Derived

class Base { 
    public: 
     virtual void bar() 
     { 
      cout << "bar\n"; 
     } 
}; 

class Derived: public Base { 
    public: 
     void foo() 
     { 
      cout << "foo\n"; 
     } 
}; 

int main() 
{ 
    Base *pb; 
    Derived *pd; 

    pb = new Derived; //Base* points to a Derived object 
    pd = dynamic_cast<Derived*>(pb); 
    pd->foo(); //outputs foo 

    pb = new Base; //Base* points to a Base object 
    pd = dynamic_cast<Derived*>(pb); 
    pd->foo(); //outputs foo, too. Why? 
} 

myślałem kiedy pb = new Derived;, pb faktycznie wskazuje na Derived obiektu leży w stercie. Po pd = dynamic_cast<Derived*>(pb);, pd również wskazuje na ten obiekt Derived, więc pd->foo() powinno być w porządku.

Ale kiedy pb = new Base;, co pb wskazuje na to Base obiekt w sterty, a następnie po pd = dynamic_cast<Derived*>(pb);, jak można pd->foo() prace? Czy obiekt dynamic_cast przekształcił obiekt w stertę w obiekt o numerze Derived?

Odpowiedz

17

W C++ każda instancja klasy ma swoją własną wersję typów danych, ale wszystkie klasy mają tę samą funkcję w pamięci (inną niż funkcje wbudowane). W przypadku, kiedy mówisz coś takiego:

pd->foo(); 

Jesteś zasadniczo nazywając Derived::foo, która jest funkcją pamięci i kompilator wie, gdzie to jest. Chodzi o to, że w ogóle nie jest zależne od pd. Jeśli jednak coś takiego:

class Derived : public Base { 
    private: 
     int a; 

    public: 
     Derived() { a = 100; } 

     void foo() { 
      std::cout<<a<<std::endl; 
     } 
}; 

Następnie pd->foo() spowoduje błąd segmentacji. W tym przypadku nie powiodło się rzutowanie dynamiczne, a po wywołaniu Derived::foo jest ono przekazywane jako obiekt 0 jako obiekt. W poprzednim przypadku wszystko było w porządku, ponieważ obiekt this nigdy nie był używany. Jednak w drugim przypadku jest on używany, a zatem powoduje błąd segmentacji.

+0

Kiedy mówię 'pd-> foo();', wtedy 'foo()' zostanie wywołane bez względu na 'pd' jest' NULL' czy nie? – Alcott

+0

@Alcott tak, ale parametr 'this' zostanie przekazany jako NULL (ponieważ jest to wartość' pd'). Stąd oczekiwana awaria, gdy wyłączysz ją, uzyskując dostęp do 'a' w przykładzie Rohana. – littleadv

+1

Jest to w dużej mierze zależne od kompilatora, a jak wspomniał już @Luchian Grigore, wszystko może się zdarzyć. Tak więc w większości przypadków tak, ale jest to coś, na co nie można liczyć. –

7

W swoim foo użytkownik nie uzyskuje dostępu do this, która w tym przypadku powinna być NULL. To właśnie powraca dynamic_cast, gdy rzutowania nie można wykonać.

Zasadniczo znajdujesz się w obszarze "niezdefiniowane zachowanie".

+0

FYI, otrzymałem 1 odpowiedź, ponieważ jest poprawna. * Ale * nadal nie widzę, że coś takiego ma szczęście. Możesz napotkać paskudne błędy, które są trudne do śledzenia, ponieważ program nie zawiesił się, gdy powinien. –

+1

@LuchianGrigore [Powodzenia] (http://en.wiktionary.org/wiki/luck#Noun) (n) 1. Coś, co dzieje się przypadkowo, przypadek. – Potatoswatter

6

Występuje niezdefiniowane zachowanie. Powinieneś sprawdzić typ zwrotu: dynamic_cast.

pd = dynamic_cast<Derived*>(pb); 

ta zwraca null, i wywołać funkcję na NULL wskaźnika. Wszystko może się zdarzyć.

+0

Ale jeśli 'pd == NULL', to dlaczego nie wystąpił błąd czasu wykonywania? – Alcott

+0

@Alcott to niezdefiniowane zachowanie. Wszystko może się zdarzyć. –

+1

@Alcott technicznie odpowiedź Luchiana jest poprawna. Ale praktycznie powodem jest to, że prostsze dla programistów kompilatorów jest po prostu zignorowanie tego, ponieważ są one dozwolone (ponieważ standard nie definiuje, co należy zrobić w takim przypadku), zamiast wykonywać skomplikowane i wydajne kontrole czasu wykonywania każdy dostęp do wskaźnika. Tak więc cokolwiek się dzieje - dzieje się i wszystko jest w porządku. – littleadv

1

Zawsze staraj się sprawdzić, czy rzutowanie zakończyło się sukcesem, zanim spróbujesz go użyć. Sądzę, że jest to zaleta używania odlewu. Możesz sprawdzić, czy się udało, czy nie.

pd = dynamic_cast<Derived*>(pb); 
if(pd!=NULL) 
    pd->foo(); 

Jeśli obsada się nie powiedzie pd ma wartość NULL. Nie używaj pd, chyba że masz pewność, że ma wartość. a potem tylko odwołuj się do niego: