2010-11-17 17 views
1

Je serais reconnaissant si quelqu'un me éclairer sur le comportement suivant - Je l'ai capturé avec un court exemple de code:Pourquoi cette simple hiérarchie de classe C++ ne présente-t-elle pas un comportement polymorphe?

//header.h 

class base 
{ 
public: 
base(int data):data1(data){} 
virtual int getData(){return data1;} 
private: 
int data1; 
}; 

class derived1 :public base 
{ 
public: 
derived1(int data):base(data-1),data2(data){} 
virtual int getData(){return data2;} 
private: 
int data2; 
}; 

class derived2 :public derived1 
{ 
public: 
derived2(int data):derived1(data-1),data3(data){} 
virtual int getData(){return data3;} 
private: 
int data3; 
}; 


//main.cpp 

derived1 d1(20); 
derived2 d2(10); 

base& baseRefd1 = d1, baseRefd2 = d2; 

cout << "call to baseRefd1.getData() yields: " << baseRefd1.getData(); 
cout << "call to baseRefd2.getData() yields: " << baseRefd2.getData(); 

derived1& derived1Refd1 = d1, derived1Refd2 = d2; 

cout << "call to derived1Refd1.getData() yields: " << derived1Refd1.getData(); 
cout << "call to derived1Refd2.getData() yields: " << derived1Refd2.getData(); 

Et la sortie:

call to baseRefd1.getData() yields: 20 
call to baseRefd2.getData() yields: 8 

call to derived1Refd1.getData() yields: 20 
call to derived1Refd2.getData() yields: 9 

Comme vous pouvez voyez, quand une référence de base est utilisée comme un handle pour un seul niveau de dérivation, nous obtenons un polymorphisme - la version de getData() appelée est celle de la classe dérivée.

Mais lorsque la même référence de base est utilisée comme un handle pour une classe dérivée de 2 niveaux dans la hiérarchie, il n'y a pas de polymorphisme - la version de getData appelée est celle de la base. Lorsqu'une référence de type derived1 est utilisée, c'est-à-dire celle du niveau intermédiaire de la hiérarchie, il n'y a pas de polymorphisme même lorsque la poignée pointe vers un niveau de classe 1.

Je suis sûr que j'ai besoin d'apprendre quelque chose de basique ici. Appréciera toute orientation.

+1

'base' [devrait] (http://www.gotw.ca/publications/mill18.htm) ont soit public destructeur virtuel ou un destructeur non virtuel protégé. – GManNickG

+6

Arrête la déclaration de plusieurs variables dans une instruction. Vous n'économisez rien, ne vous causez que des problèmes. –

Répondre

13
base& baseRefd1 = d1, baseRefd2 = d2; 

C'est le même que

base& baseRefd1 = d1; 
base baseRefd2 = d2; 
    ^baseRefd2 is not a reference! 

Vous avez besoin d'un autre esperluette:

base& baseRefd1 = d1, & baseRefd2 = d2; 
        ^this makes baseRefd2 a reference 

Ceci est l'une des meilleures raisons de suivre la règle « ne déclarer une variable à la fois , en particulier lors de la déclaration de pointeurs ou de références. "

+0

C'était incroyablement rapide. Merci beaucoup. Maintenant, j'apprécie le fait que de nombreux programmeurs placent leur '&' près du nom de la variable et non près du nom de la variable. Vraisemblablement, votre commentaire s'applique également à *. Oui? –

+0

@I Gottlieb: (Parlant pour James) Oui –

+3

Il suffit de ne pas déclarer plusieurs variables dans une seule instruction, et cela devient un non-problème. – fredoverflow

1

baseRefd2 et derived1Refd2 ne sont pas des références, ils sont des copies. La déclaration de votre variable est à blâmer

Qu'est-ce que vous avez écrit, si divisé à 2 lignes ressemble à:

base& baseRefd1 = d1; 
base baseRefd2 = d2; 

et

derived1& derived1Refd1 = d1; 
derived derived1Refd2 = d2; 

(manque NOTE ampli 2 déclaration dans chaque)

la bonne façon de les déclarer sur une seule ligne est:

base& baseRefd1 = d1, &baseRefd2 = d2; 

derived1& derived1Refd1 = d1, &derived1Refd2 = d2; 

(Notez le nouveau esperluette)

+0

Ok, ma question était clairement le manque d'expérience. –

+0

Apprécié tous vos commentaires, merci. –

3

Il est en effet possible de déclarer plusieurs références sans répéter le symbole &:

template <typename T> 
struct reference_to 
{ 
    typedef T & type; 
}; 

reference_to<base>::type baseRefd1 = d1, baseRefd2 = d2; 
+0

Cela fonctionne aussi avec 'identity' (' identity :: type'). Cependant, je pense que 'identity' est encore en C++ 0x. litb sait: en plus d'être un grand fan de GMan, il est aussi un grand fan de 'identity'. –

+0

ne serait pas 'typedef base & base_reference;' être suffisant (même si pas générique je l'admets)? –

+0

@Mat: Bien sûr, mais j'aime la généricité :) – fredoverflow