Miguel Moquillon wrote:
Laurent Deniau wrote:
Cette relation de *polymorphisme* est necessaire meme dans les langages
OO, ce n'est donc pas un critere.
Je ne comprends pas ce que tu veux dire ici.
Je veux pointer du doigt la difference entre (1)polymorphisme, (2)subtyping et (3)heritage.
(1) et (2) sont semantiquement disjoints mais se recouvre en pratique.
(3) inclus (1)+(2), mais (1)+(2) ne donne pas (3)
Le C supporte (1) et (2) au sens de Liskov mais pas (3). (cf plus bas). Quand je dis supporte, c'est qu'il y a tous les ingredients dans la norme pour etre autorise a le faire officiellement.
C:
struct Derived { struct Base; ... };
[ ... ]
Oui, en écrivant d'une certaine façon les structures de données, tu peux
effectivement implémenter une forme d'héritage et par là pouvoir simuler le
polymorphisme. Si tu veux rendre transparent ceci, tu as à écrire un
Ce n'est pas une simulation, c'est cette facon d'utiliser les aggregations qui est prevue par la norme.
framework qui implémente les mécanismes objet pour C (cf. GTK+ avec GObject
par exemple).
Je ne suis pas pret d'utiliser GObject beaucoup trop lourd et avec tres peu de check a la compilation. OOC (Object Oriented C, completement C89) est beaucoup plus simple, beaucoup plus evolue et fournit beaucoup de checks a la compilation. OOC est inspire de Java, C# et C++. Je suis aussi depuis une semaine entrain de travailler sur COS (C Object System, completement C89) qui lui est inspire d'Objective-C et de CLOS et est encore plus simple, plus flexible et plus dynamique que OOC. Les deux valent (voir surpasse ;-) ) pas mal de langages OO tres connus.
Ce qui est différent d'un "langage qui supporte nativement"
pour reprendre ton expression :).
Pour l'heritage et les autres concepts OO je suis entierement d'accord. Pour le polymorphisme, c'est natif en C, precise dans la norme et utilisable sans artifice, code, macros ou bidouille. Je crois que je l'ai deja dit ;-) et c'etait ce que je voulais souligner. Sans les precisions utiles dans la norme, ca devient une bidouille plus ou moins portable, et c'est toute la difference avec un 'support' par le langage comme tu le soulignes.
Note que si le cast te gene, print peut etre reecrit pour l'eviter:
void print(void *base) // polymorphique
{
Base b = base;
printf(b->name);
}
avec dans le main:
print(base);
print(derived);
Tout ca est tout a fait valide meme si ce n'est pas recommande ;-)
Je ne parle évidemment pas de C++ ou d'ObjC qui eux supportent les concepts
objets d'héritage, de délégation (pour ObjC) et de polymorphisme.
seulement de polymorphisme (au moins deux cas dans la norme: le
recouvrement d'aggregation a l'offset zero et les flexibles arrays).
?. Je n'appelle pas ça du polymorphisme. A la rigueur une ressemblance ou
simulation de ce dernier.
Pourtant c'est bien du polymorphisme au sens de Liskov:
Barbara Liskov, “Data Abstraction and Hierarchy,”
SIGPLAN Notices,
23,5 (May, 1988).
"What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2
of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T."
L'exemple ci-dessus de print le montre avec
o1 = derived de type S = Derived
o2 = base de type T = Base
P = print
Le programme print reste inchange et valide, on a donc bien (1) et (2).
De plus si on prend C++ pour lequel je suppose que tu admets qu'il supporte le polymorphisme, on peut ecrire le code suivant semantiquement equivalent a l'exemple ci-dessus:
Base::print()
{
printf(name);
}
et dans le main:
base.print();
derived.print();
Note que le "type checking" et le "method overriding" ne sont pas aborde dans mes propos.
Qu'est ce que tu entends par "on pense les exceptions différemment".
Dans les langages objet de masse (C++, Java, etc.), et dans d'autres, les
exceptions sont des classes d'objets à définir et à lever explicitement.
Dans Java, on doit en plus spécifier dans la signature des méthodes les
exceptions qui peuvent intervenir. En Eiffel, les exceptions ne sont que des "signaux" issus d'une violation de
contrat (bien que l'on peut en lever un explicitement). Donc, lorsque tu
écris une classe d'objets, tu penses tes cas exceptionnels (ce que devrait
être une exception) en termes de contrats.
Ce que tu veux dire, c'est qu'en fait tu ne pense plus aux exceptions, mais aux contrats et tu laisses le systeme s'occuper de transcrire la violation de ces derniers en terme d'exceptions.
a+, ld.