2012-08-15 13 views
8

Przechodzę do quizu C++. I natknąłem się na następujący kod - jest nielegalny, ale nie mogę zrozumieć, dlaczego. Czy ktoś może wyjaśnić, dlaczego ta linia:Dziedziczenie - dlaczego jest to nielegalne?

Box* b1 = s1->duplicate(); 

generuje błąd kompilatora, „nie można przekonwertować z Shape * do skrzynki”? Założono, że s1->duplicate() dzwoni Box::duplicate() ponieważ s1 faktycznie wskazuje na faktycznie wskazuje na Box - ale z powodu błędu kompilatora wygląda na to, że wywołuje Shape::duplicate().

#include <iostream> 

struct Shape 
{ 
    virtual Shape* duplicate() 
    { 
    return new Shape; 
    } 

    virtual ~Shape() {} 
}; 

struct Box : public Shape 
{ 
    virtual Box* duplicate() 
    { 
    return new Box; 
    } 

}; 

int main(int argc, char** argv) 
{ 
    Shape* s1 = new Box; 

    Box* b1 = s1->duplicate(); 

    delete s1; 
    delete b1; 
    return 0; 
} 
+1

powodu [t jego] (http://stackoverflow.com/questions/4665117/c-virtual-function-return-type). –

Odpowiedz

8

Shape::duplicates() zwraca wartość Shape*, która nie jest wartością Box*. Typ środowiska wykonawczego, który faktycznie powracasz, nie ma z tym nic wspólnego. W jaki sposób kompilator wiedział, że zwrócony Shape* wskazuje na Box?

Edit: Pomyśl o tym:

struct Shape 
{ 
    virtual Shape* duplicate() 
    { 
    return new Shape; 
    } 

    virtual ~Shape() {} 
}; 

struct Box : public Shape 
{ 
    virtual Box* duplicate() 
    { 
    return new Box; 
    } 

}; 

struct Sphere : public Shape 
{ 
    virtual Sphere* duplicate() 
    { 
    return new Sphere; 
    } 

}; 

Shape* giveMeABoxOrASpehere() 
{ 
    if (rand() % 2) 
     return new Box; 
    else 
     return new Sphere; 
} 

// 
Shape* shape = giveMeABoxOrASphere(); 
// What does shape->duplicate() return? 

Box* shape = giveMeABoxOrASphere(); 
// shoud this compile? 
+0

Hmm. Cóż - przypuszczam, że pomyślałem, że jeśli kompilator może zaakceptować 'Shape * s' = new Box', to w jakiś sposób * wie, * że' s1' jest teraz wskaźnikiem do ramki. Jest późno, myślę, że mój mózg jest smażony. . . – BeeBand

+2

@BeeBand: Istnieje istotna różnica między 'Kształt * s = nowy Box;' i 'Box * b = new Shape;'. – aschepler

+1

@BeeBand w tym przypadku możesz dowiedzieć się z 'dynamic_cast', więc informacje o typie nie zostały całkowicie utracone. To jest po prostu niedostępne dla kompilatora. –

15

Język C++ jest statycznie wpisany. Decyzje dotyczące legalności połączenia są podejmowane podczas kompilacji. Oczywiście kompilator nie może wiedzieć, że s1->duplicate() zwraca wskaźnik do obiektu Box. W tych okolicznościach byłoby nielogiczne oczekiwanie, że zaakceptuje on twój kodeks.

Tak, s1->duplicate() rzeczywiście wywołuje Box::duplicate w twoim przykładzie, ale jak możesz się spodziewać, że kompilator to wie? Można powiedzieć, że jest "oczywisty" z konkretnego przykładu, ale specyfikacja tej funkcji językowej nie stanowi wyjątku dla takich "oczywistych" przypadków.

1

do dokładnego samego powodu

Shape* s1 = new Box; 
Box* b1 = s1; 

nie kompiluje. Kompilator nie obchodzi, że s1 odnosi się do Box, ani nie obchodzi.

Jeśli wiesz, że s1 odnosi się do Box, po prostu powiedzieć:

Box *s1 = new Box; 

Uwaga na temat składni: zasady analizowania dla Box * s1; są (bardzo uproszczony):

declaration := type-name declarator ; 
declarator := name 
      | * declarator 

więc parsowanie to:

Box  *  s1  ; 
        ^^^^^^^^ 
        declarator 
^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ 
type-name   declarator 

a grupa jest Box (* (s1))

Jest uważany za najlepszy styl napisać Box *s1; ponieważ jest bardziej spójna z parsowania niż Box* s1; Jeśli zadeklarujesz więcej niż jedną zmienną w jednej deklaracji, składnia Box* może być mylące:

Box* x, y; 

x jest wskaźnik do Box, ale y jest Box, ponieważ podczas analizy jest:

Box (*x), y; 
+0

ok dzięki za napiwek ponownie. parsowanie. . . – BeeBand

Powiązane problemy