2016-07-03 6 views
3

Il ne se est produit pour moi que C++ a covariance pointeur et donc vous vous tirer dans laisse la jambe comme ceci:C++ pointeur covariance

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 
} 

Est-ce un comportement non défini? Est-il possible de l'empêcher ou au moins avoir un avertissement du compilateur?

Répondre

2

Oui, ce comportement n'est pas défini.

Et non, un compilateur C++ typique, à ce stade, est peu susceptible d'être capable d'identifier quelque chose qui mérite un diagnostic, ici. Mais, les compilateurs C++ deviennent plus intelligents chaque année qui passe. Qui sait ce que sera l'état des choses, des années à partir de maintenant ...

Cependant, un petit bémol:

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

Non, ce n'est pas le premier élément. c'est le deuxième élément. b[0] serait le premier élément. De plus, b n'est pas un tableau, c'est un pointeur. Et c'est un pointeur vers un seul élément. Ce n'est pas un pointeur vers un tableau à deux éléments.

Et c'est de là que vient le comportement indéfini.

La raison pour laquelle ce n'est pas un tableau est parce que:

Base * b = children1; 

children1 désintègre à un Child1 *. Si c'est là que l'affaire s'est terminée, vous pourriez dire que b serait un pointeur vers un tableau à deux éléments.

Mais ce n'est pas là que les choses se sont terminées. Le pointeur pourri a été lancé à Base *. Vous pouvez implicitement transformer un pointeur en sous-classe en pointeur vers une superclasse. Mais (en parlant vaguement maintenant) vous ne pouvez pas lancer un pointeur vers un tableau de sous-classes à un tableau de superclasses. Par conséquent, b est, strictement, un pointeur vers un seul élément, et b[1] devient un comportement indéfini.

+0

J'ai l'habitude d'appeler b [0] l'élément zéro. Et effectivement faire un tableau n'est pas nécessaire. Je peux produire single Child1 child1; Base * b = & enfant1; * b = enfant2; et obtenir le même comportement indéfini, je ne peux pas? – Amomum

+0

Non, ce serait un comportement parfaitement défini. –

+0

@Amomum: Bien sûr, ce n'est pas parce que c'est bien défini que c'est une bonne idée. Cela ne signifie pas non plus qu'il fait ce que vous pensez qu'il fait. –