2010-07-25 2 views
3

Je suis tellement confondu avec la sortie du code ci-dessous:Confus avec C++ Héritage

#include <iostream> 

using namespace std; 

class Parent 
{ 
public: 
    Parent() : x(2) { } 
    virtual ~Parent() { } 

    void NonVirtual() { cout << "Parent::NonVirtual() x = " << x << endl; } 

private: 
    int x; 
}; 

class Child : public Parent 
{ 
public: 
    Child() : x(1) { } 
    virtual ~Child() { } 

    void NonVirtual() { cout << "Child::NonVirtual() x = " << x << endl; } 

private: 
    int x; 
}; 

int main() 
{ 
    Child c; 
    Parent* p = &c; 

    c.NonVirtual(); // Output: Child::NonVirtual() x = 1 
    p->NonVirtual(); // Output: Parent::NonVirtual() x = 2 
    // Question: are there two x'es in the Child object c? 
    //   where is x = 2 from (we have not defined any Parent object)? 

    cout << sizeof(c) << endl; 
    cout << sizeof(*p) << endl; 

    return 0; 
} 
+1

Le 'sizeof (c)' étant 12 reflète le vptr et les 2 x. Il n'y a qu'un seul vptr, pas deux. –

+0

@Matt, vous avez raison. J'ai fait une erreur, ce qui m'a désorienté. Merci. –

Répondre

5

Le code ci-dessus illustre le fait que, en C++, seules les fonctions marquées virtual sont outrepassée. Ce que vous avez ici est ombrageant, ne dérogeant pas. Dans l'overriding et l'héritage, le comportement est basé sur runtime type qui est le comportement d'héritage normal que vous attendez, mais si vous ne le déclarez pas virtuel, alors le comportement est basé uniquement sur à la compilation de type (ie type déclaré). Puisque p est déclaré de type Parent *, il utilise l'implémentation dans Parent, alors que c est déclaré être de type Child, et utilise donc la version donnée par le type Child. Si vous avez déclaré la méthode virtuelle, dans les deux cas, elle recherchera la version appropriée de la fonction lors de l'exécution et appellera la version donnée dans la classe Child. Je devrais aussi ajouter que vous avez deux variables x différentes ... si vous voulez partager des variables entre une classe de base et une classe dérivée, vous devriez le marquer "protégé" dans la classe de base (même si je dirais que il est généralement de mauvaise conception pour le faire). La variable x que vous déclarez dans Child est une variable différente de celle de Parent. Rappelez-vous que x est privé dans Parent, et que le nom x n'a donc aucune signification dans Child jusqu'à ce que vous ayez créé une deuxième variable nommée x dans Child.

+0

@Michael: Je suis d'accord avec ce que vous avez dit. mais mes questions sont (aussi dans les commentaires de code): y a-t-il deux x'es dans l'objet Child c? où est x = 2 de (puisque nous n'avons défini aucun objet Parent)? –

+0

@Peter Lee, oui, et "x" fera référence à celui qui est visible dans la portée de cette classe. –

+0

@Michael: Je suis également d'accord avec ce que vous avez dit dans le deuxième paragraphe nouvellement ajouté, mais toujours la question: où est x = 2 de? Est-ce d'un objet Parent? Si oui, où est l'objet Parent (puisque nous n'avons défini aucun objet Parent)? –

0

Il n'y a rien à confondre ici. Lorsque vous appelez une fonction non virtuelle, quel que soit le type auquel elle a été affectée, les méthodes de ce type sont invoquées.

Ici, Parent * p et l'adresse de l'enfant sont les mêmes, mais l'affectation est au Parent. Ainsi, toutes les méthodes non virtuelles invoquent les méthodes du parent et non celle de l'enfant. N'oubliez pas que lorsque vous utilisez l'héritage (en particulier un héritage public), l'enfant dérive automatiquement toutes les méthodes et tous les membres du parent. Ainsi, même si, vous affectez le pointeur de l'enfant, mais l'affectation a lieu au type du parent. La seule chose que vous devez vous assurer est que vous ne supprimez pas P ici comme P et enfant C, partage la même adresse.