2012-08-15 4 views
8

Ich gehe durch ein C++ Quiz. Und es kam der folgende Code - es ist illegal, aber ich kann nicht verstehen, warum. Kann mir jemand erklären, warum diese Linie:Vererbung - warum ist das illegal?

Box* b1 = s1->duplicate(); 

den Fehler Compiler generiert, „kann nicht von Shape * zu Box umwandeln“? Ich nahm an, dass s1->duplicate() ruft Box::duplicate(), weil s1 tatsächlich auf eine Box zeigt - aber vom Compiler-Fehler sieht es aus wie es ruft 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

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

Antwort

8

Shape::duplicates() gibt eine Shape* zurück, die keine Box* ist. Der tatsächlich zurückgegebene Laufzeittyp hat damit nichts zu tun. Wie kann der Compiler wissen, dass die zurückgegebene Shape* tatsächlich auf eine Box zeigt?

Edit: Denken Sie darüber nach:

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. Nun - ich denke, ich dachte an die Zeilen, wenn der Compiler 'Shape * s' = neue Box' akzeptieren kann, dann würde es * irgendwie * wissen, dass' s1' jetzt ein Zeiger auf eine Box ist. Es ist spät, ich denke, mein Gehirn ist gebraten. . . – BeeBand

+2

@BeeBand: Es gibt einen wichtigen Unterschied zwischen 'Shape * s = new Box;' und 'Box * b = new Shape;'. – aschepler

+1

@BeeBand in diesem Fall könnte man mit einem 'dynamic_cast' herausfinden, so dass die Typinfo nicht komplett verloren geht. Es ist nur für den Compiler nicht verfügbar. –

15

C++ - Sprache ist statisch typisiert. Die Entscheidungen über die Rechtmäßigkeit Ihres Anrufs werden zum Zeitpunkt der Kompilierung getroffen. Der Compiler kann natürlich nicht wissen, dass s1->duplicate() einen Zeiger auf ein Objekt Box zurückgibt. Unter diesen Umständen wäre es unlogisch zu erwarten, dass Ihr Code akzeptiert wird.

Ja, s1->duplicate() nennt in Ihrem Beispiel tatsächlich Box::duplicate, aber wie erwarten Sie, dass der Compiler das weiß? Man kann sagen, dass es von Ihrem spezifischen Beispiel "offensichtlich" ist, aber die Spezifikation dieses Sprachmerkmals macht keine Ausnahme für solche "offensichtlichen" Fälle.

1

Für die exakt gleichen Grund

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

nicht kompiliert. Dem Compiler ist es egal, dass sich s1 auf einen Box bezieht, noch sollte es sich interessieren.

Wenn Sie wissen, dass s1 auf eine Box bezieht, nur sie sagen:

Box *s1 = new Box; 

Ein Hinweis zu Syntax: Die Parsing-Regeln für Box * s1; sind (sehr vereinfacht):

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

so die Parsing ist:

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

und die Gruppierung ist Box (* (s1))

Es ist am besten Stil betrachtet Box *s1; zu schreiben, weil es im Einklang mit der Analyse als Box* s1; Wenn Sie mehr als eine Variable in einer Erklärung deklarieren, die Box* Syntax kann verwirrend sein, ist:

Box* x, y; 

x ist ein Zeiger auf Box, aber y ist ein Box, da das Parsing ist:

Box (*x), y; 
+0

ok danke für den Tipp re. analysieren. . . – BeeBand