2016-07-03 16 views
3

Es fiel nie, dass c mir ++ Zeiger Kovarianz hat und damit man sich in das Bein wie folgt schießen lässt:C++ Zeiger Kovarianz

struct Base 
{ 
    Base() : a(5) {} 
    int a;  
}; 

struct Child1 : public Base 
{ 
    Child1() : b(7) {} 
    int b; 
    int bar() { return b;} 
}; 

struct Child2 : public Base 
{ 
    Child2(): c(8) {} 
    int c; 
}; 

int main() 
{ 
    Child1 children1[2]; 

    Base * b = children1; 

    Child2 child2; 

    b[1] = child2; // <------- now the first element of Child1 array was assigned a value of type Child2 

    std::cout << children1[0].bar() << children1[1].bar(); // prints 57 
} 

Ist es ein nicht definiertes Verhalten? Gibt es eine Möglichkeit, dies zu verhindern oder zumindest eine Warnung vom Compiler zu erhalten?

Antwort

2

Ja, dies ist ein undefiniertes Verhalten.

Und nein, ein typischer C++ - Compiler, an diesem Punkt, ist wahrscheinlich nicht in der Lage, etwas zu identifizieren, das eine Diagnose verdient, hier. Aber C++ - Compiler werden mit jedem Jahr intelligenter. Wer weiß, was der Stand der Dinge sein, Jahre ...

jedoch eine kleine Wortklauberei:

b[1] = child2; // <------- now the first element of Child1 array was assigned... 

Nein, das nicht das erste Element ist. es ist das zweite Element. b[0] wäre das erste Element. Außerdem ist b kein Array, es ist ein Zeiger. Und es ist ein Zeiger auf ein einzelnes Element. Es ist kein Zeiger auf ein Zwei-Elemente-Array.

Und das ist, wo das undefinierte Verhalten herkommt.

Der Grund ist es kein Array ist, weil:

Base * b = children1; 

children1 zu einem Child1 * zerfällt. Wenn dort die Affäre endete, könnte man sagen, dass b ein Zeiger auf ein Zwei-Elemente-Array wäre.

Aber das ist nicht, wo die Dinge endeten. Der verfallene Zeiger wurde dann zu einem Base * geworfen. Sie können einen Zeiger auf eine Unterklasse implizit in einen Zeiger auf eine Oberklasse umwandeln. Aber (jetzt losgelöst) können Sie keinen Zeiger auf ein Array von Unterklassen zu einem Array von Superklassen werfen. Daher ist b streng genommen ein Zeiger auf ein einzelnes Element und b[1] wird zu undefiniertem Verhalten.

+0

Ich bin es gewohnt, b [0] das Nullelement zu nennen. Und tatsächlich ist das Erstellen eines Arrays nicht notwendig. Ich kann einzelne Child1 child1 produzieren; Basis * b = & child1; * b = Kind2; und bekomme das gleiche undefinierte Verhalten, kann ich nicht? – Amomum

+0

Nein, das wäre ein perfekt definiertes Verhalten. –

+0

@Amomum: Natürlich, nur weil es gut definiert ist, bedeutet es nicht, dass es eine gute Idee ist. Es bedeutet auch nicht, dass es tut, was du denkst. –