Surcharge et Redéfinition
Algorithme de résolution de la surcharge
L’algorithme de surcharge peut produire trois résultats différents :
– Erreur de compilation si aucune fonction ne correspond à l’appel
– Erreur de compilation si plusieurs fonctions sont à la même distance minimale de l’appel
– Un résultat correct si une seule fonction est à une distance minimale de l’appel.
Soit l’appel o.f(p1,….pn) ou T.f(p1,….pn), on doit maintenant sélectionner la signature du service f
qui correspond au mieux à cet appel. On notera T1 le type déclaré de p1,…. TN le type déclaré de
pn.
1. L’étape initiale consiste à collecter l’ensemble des signatures qui correspondent à un service de nom f. Pour cela, il faut déterminer le type déclaré de O, ce type sera appelé T. Dans le cas, de la sélection d’un service de classe, le type de la classe correspondra au type déclaré T. Une fois le type déclaré, on collecte l’ensemble des signatures des services de nom f en partant du type T et en remontant en considérant tous les services de nom f dans tous les sur types de T et tous les sur….sur type de T jusqu’en qu’il ne soit plus possible de remonter au sur-type. On obtient à cette étape un ensemble de signatures de services qui ont le nom f.
2. La deuxième étape est une étape de filtrage qui consiste à supprimer l’ensemble des services sélectionnés qui n’ont pas la bonne arité (c’est à dire le bon nombre de paramètres) ainsi que les services de signature f qui ne sont pas compatibles avec l’appel. Pour chacune des signatures des fonctions f restantes on procède de la façon suivante, soit une signature f(S1,…Sn). On doit savoir si elle est compatible avec l’appel f(p1,….pn). Pour un Si à une place i, on regarde si il est compatible avec Ti le type déclaré du paramètre pi de l’appel à la place i. ON CONSIDERE UNIQUEMENT LE TYPE DECLARE Ti du paramètre pi. JAMAIS LE TYPE REEL du paramètre pi, ne sera pris en compte. Un paramètre Ti est compatible avec un paramètre Si si :
■ si Ti et Si sont deux types primitifs, alors on peut convertir un Ti en un Si, soit par promotion soit par cœrcition
■ si Ti et Si sont deux types non primitifs, alors il faut que Ti soit égal à Si ou bien que Ti soit un sous type de Si, il faut que Ti hétire de Si.
■ Sinon les deux types sont incompatibles.
Une fois cette étape réalisée, il ne reste plus qu’un ensemble de signatures de fonctions qui sont
toutes compatibles avec l’appel. A cette étape, si aucune signature de fonction n’est compatible avec l’appel, il y a une erreur de compilation puisque aucune ne correspond à l’appel.
3. Après l’étape 2, il faut maintenant choisir la signature de fonction qui est la plus proche de l’appel si bien sûr plusieurs sont possibles. Pour cela, pour chacune signature un évalue une fonction de coût de conversion. Cette fonction de conversion se fait en évaluant pour chaque paramètre le coût de conversion et la fonction associée à la signature est la somme des coûts de conversion. Sans entrer dans les détails, une cœrcition à un coût supérieur à une promotion, si le type de la signature et le type de l’appel sont les mêmes le coût de conversion est nul, en cas de compatibilité par héritage, le coût dépend de la profondeur de l’arbre d’héritage. Après cette étape, chacune des signatures sélectionnées est compatible avec l’appel et un coût de conversion lui est associé.
4. Si il existe une seule signature de coût de conversion minimale, c’est à dire si il existe une unique signature de fonction qui est la plus près de l’appel, alors le résultat de
l’algorithme de sélection de la surcharge produit une unique signature f(S1,…Sn).
Si par contre il existe plusieurs signatures qui sont à une même distance minimale de l’appel, la résolution de la surcharge est considérée comme ambiguë est une erreur de compilation est produite.
Exemple des différents résultats sélectionnés par l’algorithme de résolution de la surcharge.
public class A{}….. public class B extends A{……}
public class Exemple {
public double f(int n, double d){……}
public double f(double d, int t) {…….}
public double f(A a){……}
public double f(B b){…….}
}
Différents exemple d’appels et la signature sélectionnée
Exemple.f(3) ;// il n’existe aucune signature d’arité 1 avec un type primitif compatible avec int.
// un type primitif comme le type associé à 3 ne peut être compatible avec un type
// d’instance…………..ERREUR DE COMPILATION, appel incompatible int i = 3 ; int j = 2 ; Exemple.f(i,j) ;
Sur cet appel deux signatures pour la fonction f sont compatibles, il s’agit des signatures double f(int n, double d) , double f(int n, double d). Ces deux fonctions ont le même coût de conversion par rapport à l’appel f(int, int). Leur coût est égal à une promotion de int à double. Dans ce cas, le résultat produit par l’algorithme de sélection est ERREUR DE COMPILATION résolution ambiguë, car plusieurs signatures de fonction sont à une distance minimale de l’appel.
Int i, double d ; Exemple.f(i,d) ; // La signature sélectionnée est double f(int n, double d). A b = new B() ; Exemple.f(b);
Le type réel de b est B mais son type déclaré est A, comme la signature sélectionnée ne dépend pas du type réel des paramètre, la signature sélectionnée par l’algorithme est bien double f(A a).
La redéfinition.
La redéfinition est la possibilité d’utiliser exactement la même signature pour définir un service dans un type et dans un sous type. Le type de retour du service doit être le même, mais la visibilité peut changer.