OVH Cloud OVH Cloud

Question entretien C++ - doutes.

95 réponses
Avatar
David Fleury
Bonjour,

voici une des questions que je pose pour lors de mes entretiens pour des
développeurs avec expérience orienté C++

"Ecrire une fonction qui renverse une chaine (ex: ABC -> CBA, AE -> EA, ...)

Après avoir posé la question deux ou trois fois déjà, je suis surpris
des réponses (souvent fausses). Je laisse volontairement le choix au
candidat d'utiliser (char* ou std::string) et/ou une fonction modifiant
la chaine entrée ou retournant une nouvelle chaine, et l'utilisation de
la STL est autorisé.

A votre avis, est-ce que la question n'est pas trop mauvaise ?

David.

PS : Dans le même genre, il y a la fonction EstPalindrome qui semble
bloquer.

10 réponses

6 7 8 9 10
Avatar
James Kanze
On Oct 31, 2:57 pm, ld wrote:
On 31 oct, 11:07, James Kanze wrote:
> où tu n'as pas de contrat, l'idiome classique serait :



>     class Base  // ou Interface, comme tu veux...
>     {
>     public:
>         Base* function()
>         {
>             //  ...
>             return doFunction() ;
>         }



>     private:
>         virtual Base* doFunction() = 0 ;
>     } ;



>     class Derived : public Base
>     {                   // ou class Implementatio n, si tu préfère.
>     public:
>         Derived* function()     // Si on veut ; dans mon ex périence,
>         {                       // c'est rarement nécessaire.
>             // ...
>         }



>     private:
>         virtual Base* doFunction()
>         {
>             return function() ;
>         }
>     } ;



> En fait, j'ai utilisé ce modèle beaucoup plus souvent pour des
> raisons d'optimisation que pour des raisons de typage,



C'est pourtant la facon classique de faire. De mon cote je
prefere la facon suivante, ca pollue moins l'interface:



struct Interface {
   virtual Interface* function() = 0 ;
};



class Concrete : public virtual Interface {
  Concrete* func() {
     func_pre();
     ...
     func_post();
  }



protected:
  void func_pre() { ... }
  void func_post() { ... }



private:
  virtual Interface* function() { return func(); }
};



Ce qui ne fait pas du tout la même chose. Le contrat, c'est bien
l'interface qui le définit, et qui donc l'enforce. Compter sur
la classe dérivée de l'enforcer ; autant ne pas l'avoir.

> par exemple, quand la fonction publique est operator[] sur
> une classe qui implémente quelque chose comme vector (où le
> coût d'un appel virtuel, et surtout l'impossibilité de
> générer la fonction inline que cela implique, pourrait avoir
> des consequences réeles sur les performances des
> applications).



Ce n'est pas la seule raison. Ou mets-tu les contrats de la
classe si tu n'as pas de version publique non virtuelle dans
ta classe?



La classe d'interface, c'est bien la même. Mais typiquement, la
classe dérivée n'a pas de fonction publique avec le même nom que
celle de l'interface ; la plupart du temps, la classe dérivée
n'a pas de fonction publique du tout.

L'avantage de ma version (surtout quand on doit l'expliquer a
des etudiants avec peu de background OO), c'est que
l'Interface est equivalente a une API C, ni plus ni moins, et
que la partie publique de la classe est equivalent a une
classe monomorphique. Apres on rajoute la colle entre
l'interface et la classe qui devient polymorphique et
exportable.



Le desavantage de ta version, c'est qu'il n'enforce pas le
contrat correctement. On pourrait toujours en dériver une
deuxième classe qui n'implémente pas le même contrat.

> Sinon, typiquement, la fonction publique dans Base
> s'appellera quelque chose comme getBase, et celle dans
> Derived getDerived.



? J'utilise tres tres rarement des getters ou des settesr si
c'est ce dont tu parles. Je n'aime pas ca.



Moi non plus. Je prenais ça simplement comme exemple du nom. Une
fonction qui garantit contractuellement à renvoyer un Derived a
un contrat différent d'une fonction qui garantit
contractuellement une Base ; elle a donc un autre nom.

[...]
> Et alors, est-ce que le problème n'est pas plutôt du à la
> virtualité en soi, qui empêche (en général) la génération



Je considere qu'un appel virtuel en presence d'heritage simple
comme etant l'unite a payer puisque je favorise la
flexibilite, donc le polymorphisme (sinon j'utiliserais Fortan
ou C).



Oui, mais c'est elle qui introduit la plus grande différence
dans les temps d'exécution.

[...]
> Comment est-ce que je peux me documenter sur ce que toi, tu
> entends par un « vrai » héritage multiple ? en quoi est-ce
> que l'héritage multiple de C++ (ou de Java) n'est pas vrai ?



C++ supporte l'heritage multiple d'implementation (ce que j'ai
appelle "vrai" pour simplifier)



OK. J'avais mal compris. Je croyais avoir entendu un criticisme
de l'héritage multiple C++ comme n'étant pas « vrai » (un peu
comme on entend le criticisme que le OO du C++ n'est pas
« vraie » des avocats de Java, et que le OO du Java n'est pas
vraie des Eiffelistes).

[...]
> > L'heritage multiple de Java peut se faire en C assez
> > facilement, et sans besoin de code supplementaire et avec
> > une excellente performance. Celui de C++, c'est exclus.



> Et quelle est la différence ? Il te faut bien une vtable, ou
> quelque chose d'équivalent, pour chaque interface dont tu
> dérives.



Dans le cas de Java, elle peut etre compacte et unique.



Je ne crois pas. Et la documentation des JVM que j'ai pu trouver
ne m'a pas indiqué autrement.

> La seule différence réele que je vois, c'est que la JVM a
> deux instructions différentes : invokeinterface et
> invokevirtual, selon qu'on appelle un fonction à travers une
> interface ou non. Quand tu invoques à travers un pointeur à
> une classe, donc, la VM ou le compilateur sait d'avance
> qu'il n'a pas besoin d'ajuster le pointeur. Quand tu
> invoques à travers une interface, en revanche, qu'est-ce que
> la VM fait de moins que quand tu appelles une fonction C++
> depuis une classe de base virtuelle ?



tout est explique dans le papier de Nathan, le mien ou l'ABI
du C++ a la section virtual call.



J'y jetterai un coup d'oeil.

> Ensuite, évidemment, il y a des questions d'optimisation. Où
> Java a l'avantage par rapport à certains compilateurs C++
> d'avoir accès aux données propre à l'exécution.



Quand on utilise les interfaces, il ne peut pas faire grand
chose de plus que C++.



Oui et non. Si on parle de l'optimisation, l'optimisateur Java a
l'avantage qu'il a accès à des données réeles (genre profiling)
de l'exécution, et qu'il connaît la machine exacte sur laquelle
tourne l'exécutable. En contrepartie, évidemment, il ne peut pas
prendre trop de temps dans l'optimisation, tandis qu'un
compilateur C++ a beaucoup plus de libertés de ce côté-là.

Évidemment, un autre avantage des optimisateurs Java, c'est
qu'on y a investi beaucoup plus d'argent:-).

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
espie
In article <ge96c3$18m$, Alex wrote:
Mais si tu copies d'après les sources de Linux ou Windows, ou depuis google
groups venant de divers gurus (Stroustrup et autres), tu ne feras
certainement pas mieux en partant de zéro et tu perdras du temps.



TU ne feras certainement pas mieux.

Pour ma part, vu la qualite moyenne du code en provenance de linux, c'est
parfois une bonne idee de refaire des choses.

Cote droit d'auteur, fais-tu les choses legalement ? reprendre du code GPL
dans un projet commercial, en esperant que ca ne se verra pas trop, ca n'est
pas bien. Tu credites les auteurs originaux, au moins ?
Avatar
ld
On 31 oct, 20:38, James Kanze wrote:
> C'est pourtant la facon classique de faire. De mon cote je
> prefere la facon suivante, ca pollue moins l'interface:
> struct Interface {
>    virtual Interface* function() = 0 ;
> };
> class Concrete : public virtual Interface {
>   Concrete* func() {
>      func_pre();
>      ...
>      func_post();
>   }
> protected:
>   void func_pre() { ... }
>   void func_post() { ... }
> private:
>   virtual Interface* function() { return func(); }
> };

Ce qui ne fait pas du tout la même chose. Le contrat, c'est bien
l'interface qui le définit, et qui donc l'enforce. Compter sur
la classe dérivée de l'enforcer ; autant ne pas l'avoir.



Ca depend de quoi on parle. Si on parle des interfaces a-la-Java, il
n'y a pas le choix puisque les interfaces n'ont pas de donnees (et en
principe pas d'implementation), donc tes //... dans Base::function ne
peuvent etre que tres limites. Si on parle des contrat a-la-Eiffel,
les contrats sont dans toutes les classes, inclues les interfaces avec
la regle de disjonction pour l'heritage des pre-conditions et de
conjonction pour l'heritage des post-conditions. Donc en C++ il
faudrait faire:

bool Derived::func_pre(bool assert) {
bool contract = func_pre_cond || Base::func_pre(false);

if (assert) assert( contract == true );

return contract;
}

bool Derived::func_post(bool assert) {
bool contract = func_post_cond && Base::func_post(false);

if (assert) assert( contract == true );

return contract;
}

Ca fait rapidement beaucoup de code! Donc je pars du principe que
chaque classe est responsable de ses donnees et c'est tout. Ce qui
veut dire que si les appels virtuels se chaines, les contrats aussi
sous forme conjonctive (toujours). Ce n'est pas l'ideal pour
determiner la responsabilite du fautif, mais c'est suffisant dans la
plus part des cas. De toute facon, la facon de Eiffel non plus n'est
pas ideale pour determiner correctement les responsabilites, voir:

R.B. Findler, M. Latendresse, and M. Felleisen.
"Behavioral Contracts and Behavioral Subtyping".
9th ACM Sigsoft International Symposium on Foundations
of Software Engineering, 2001

Donc je prefere favoriser la localite (cohesion) des contrats.

> > Et alors, est-ce que le problème n'est pas plutôt du à la
> > virtualité en soi, qui empêche (en général) la génération
> Je considere qu'un appel virtuel en presence d'heritage simple
> comme etant l'unite a payer puisque je favorise la
> flexibilite, donc le polymorphisme (sinon j'utiliserais Fortan
> ou C).

Oui, mais c'est elle qui introduit la plus grande différence
dans les temps d'exécution.



Certe, la virtualite par rapport a l'inlining, c'est un facteur 2-3
pour une fonction triviale. Mais c'est le prix a paye pour la
flexibilite du code (reduction du couplage) et c'est inevitable.
Tandis que le cout introduit par l'heritage multiple d'implementations
pourrait etre optimise et ne pas ralentir la virtualite dans le cas
particulier de l'heritage multiple d'interfaces (pas de donnees
membres).

a+, ld.
Avatar
pjb
Wykaaa writes:
Uun développeur ne crée d'ailleurs jamais d'algorithme mais ne fait
qu'utiliser des algorithme existants en les adaptant.



Ce n'est pas un programmeur alors.


--
__Pascal Bourguignon__ http://www.informatimago.com/

This is a signature virus. Add me to your signature and help me to live.
Avatar
pjb
Wykaaa writes:
D'ailleurs on peut écrire : "0123456789ABCDEF"[i].
Je peux te garantir que ceci trouble même certains programmeurs C très
expérimentés.



Et aussi: i["0123456789ABCDEF"]
ce qui est encore plus troublant ! :-)

--
__Pascal Bourguignon__ http://www.informatimago.com/

This is a signature virus. Add me to your signature and help me to live.
Avatar
pjb
James Kanze writes:
On Oct 30, 4:31 pm, ld wrote:
Imagine toi assister à une réunion en Mandarin (à moins que tu
ne le parles?), il te sera difficile de participer...



Certes, mais pour une poste en France, je m'attendrais qu'on
discute soit en français, soit en anglais (dans le contexte d'un
projet international). Ou sinon qu'une connaissance de Mandarin
soit exigée d'avance, dans quel cas, je ne me postulerais même
pas. Mais je ne vois pas trop le rapport avec des compétences
C++.



L'incapacité à comprendre une analogie pourrait facilement être
éliminatoire dans un entretient d'embauche, AMA.

--
__Pascal Bourguignon__ http://www.informatimago.com/

PLEASE NOTE: Some quantum physics theories suggest that when the
consumer is not directly observing this product, it may cease to
exist or will exist only in a vague and undetermined state.
Avatar
Jean-Marc Bourguet
Wykaaa writes:

Michael DOUBEZ a écrit :
Wykaaa a écrit :
[snip]
Je voulais dire, en fait :
pt étant un nom de tableau peut-on écrire pt++ ?
La réponse est évidemment non puisqu'un nom de tableau est un pointeur
constant sur le premier élément du tableau.



Comme d'autre l'ont signalé, le om d'un tableau est de type tableau et
non pas de type pointer. Pour des raisons historiques (K&R C), le tableau
se converti implicitement en pointeur mais il reste un tableau.





Et cette conversion implicite (ailleurs qu'en argument de sizeof et de & en
C, en C++ je n'entre pas dans les règles de surcharge et de déduction des
templates qui offrent d'autres cas où la conversion implicite ne se fait
pas) ne se fait pas uniquement pour variables de type tableau mais pour
tous les objets de ce type.

int x[10][10];
struct S {
int t[10];
} y;

x est converti en un int (*)[10], et x[5] et y.t en un int* dans la plupart
des expressions, mais ce sont bien des expressions qui designent des objets
de types int[10][10] et int[10].

D'ailleur:
int * const ptr;
const int tab[42];
int * const ptr=tab;

sizeoff(ptr)!=sizeof(tab)



Mais tab[5] == *(tab+5)
Donc ta dernière phrase joue sur les mots car, dans la partie droite de ma
comparaison, le nom du tableau est bien utilisé comme un pointeur !



De même que dans

float f;

int i;

printf("%g %gn", f, i+1.0);

f et i sont utilisés comme des doubles. Les tableaux C sont déjà une
source de confusion assez grande comme cela; faire croire que le nom d'un
tableau a un type pointeur ne fait qu'augmenter la confusion -- en
particulier en C++.

Et là c'est encore plus fort car tab+5 signifie :
&tab[0] + 5*sizeof(type_des_elements_du_tableau).



Non. Ca signifie &tab+5 (ce qui est équivalent à

(T*)((unsigned char*)&tab[0] + 5 * sizeof(T))

ce que tu voulais dire sans doute...).

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
James Kanze
On Nov 1, 12:11 pm, (Pascal J. Bourguignon)
wrote:
Wykaaa writes:
> Uun développeur ne crée d'ailleurs jamais d'algorithme mais
> ne fait qu'utiliser des algorithme existants en les
> adaptant.



Ce n'est pas un programmeur alors.  



Justement, c'est un programmeur. Mais ce n'est qu'un
programmeur. En anglais, on distingue bien entre « programmer »,
« analyst » et « analyst-programmer ». (On ajoute aussi par fois
le mot « system », avec une signification bien différente selon
qu'il s'applique à « analyst » ou « programmer ».)

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Jean-Marc Bourguet
Wykaaa writes:

Jean-Marc Bourguet a écrit :
Wykaaa writes:





Les tableaux C sont déjà une source de confusion assez grande comme
cela; faire croire que le nom d'un tableau a un type pointeur ne fait
qu'augmenter la confusion -- en particulier en C++.



Désolé, mais "un type pointeur", ça ne veut rien dire puisque les pointeurs
sont... typés.



Bizarre, l'expression se trouve dans les normes C90, C++98, C99 et le
dernier brouillon disponible de C++0X avec le sens que je lui donne ici.

Je voulais dire que le nom de tableau est manipulé comme un pointeur
(d'ailleurs du type des éléments).



Pas dans tous les contextes:

void f(int (&t)[10]);
template <typename T>
void g(T&);

int tab[10];

Dans aucune des expressions sizeof tab, &tab, typeid(tab), f(tab), g(tab)
tab n'est converti en un pointeur vers son premier élément.

S'il n'y avait pas cette "équivalence" pointeur/tableau, il n'y aurait pas
besoin des [] pour indiquer à delete que le pointeur pointe sur un tableau
alloué dynamiquement afin d'appeler les destructeurs pour chaque instance.



J'ai du mal à comprendre ce que tu veux dire. Dans

int* t = new int[10];
delete[] t;

la conversion implicite d'un tableau en un pointeur n'intervient pas. On
la virerait et on permettrait d'appliquer [] sur les tableaux qu'il y a peu
de choses qui changerait à part les appels de fonctions.

Denis Ritchie qui parlait de ces traits de langage (en y incluant les
chaînes de caractères) dans l'HOPL (History Of Programming Lnguages) de
1993 et en vantant les mérites de "l'homogénéisation" entre chaînes de
caractères, tableaux, etc. en C par rapport à PL/1 ne devrait finalement
pas être si fier car, au total, tout ceci ne peut pas toujours être
optimisé (comme il le reconnaissait) et sème une grande confusion pour les
débutants (et même souvent pour les développeurs expérimentés).



Tu peux donner une référence plus précise? Je ne vois pas de comparaison
directe sur ce point avec PL/I dans le texte (je vois Pascal et Algol 68)
-- que ce soit l'article ou la présentation -- et s'il a l'air bien content
de l'unification entre les chaînes et les tableaux, il me semble que ce
n'est pas tant à cause des qualités de ceux-ci, que parce que ça simplifie
l'ensemble de ne pas avoir un type chaîne distinct. Et si effectivement il
trouve que "les utilisations principales des tableaux et des chaînes sont
couvertes par un mécanisme uniforme et simple", je ne suis pas sur que la
conversion implicite des tableaux en pointeurs n'est pas un des "accidents
historiques qui ont exacerbé leur difficulté" (le mécanisme est pour moi
l'arithmétique des pointeurs, la conversion n'est pas un erreur -- son
objectif était de simplifier la conversion du code B existant).

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
James Kanze
On Nov 1, 9:56 am, ld wrote:
On 31 oct, 20:38, James Kanze wrote:
> > C'est pourtant la facon classique de faire. De mon cote je
> > prefere la facon suivante, ca pollue moins l'interface:
> > struct Interface {
> >    virtual Interface* function() = 0 ;
> > };
> > class Concrete : public virtual Interface {
> >   Concrete* func() {
> >      func_pre();
> >      ...
> >      func_post();
> >   }
> > protected:
> >   void func_pre() { ... }
> >   void func_post() { ... }
> > private:
> >   virtual Interface* function() { return func(); }
> > };



> Ce qui ne fait pas du tout la même chose. Le contrat, c'est
> bien l'interface qui le définit, et qui donc l'enforce.
> Compter sur la classe dérivée de l'enforcer ; autant ne pas
> l'avoir.



Ca depend de quoi on parle. Si on parle des interfaces
a-la-Java, il n'y a pas le choix puisque les interfaces n'ont
pas de donnees (et en principe pas d'implementation), donc tes
//... dans Base::function ne peuvent etre que tres limites.



On parle, il me semble, des interfaces. Dans le sens de la
conception logicielle. Que l'« interface » de Java ne permet à
implémenter que d'une façon limitée, parce qu'elle ne permet pas
la validation du contrat. Mais c'est bien l'interface qui
définit le contrat. Sinon, ça n'a pas de sens.

Si on parle des contrat a-la-Eiffel, les contrats sont dans
toutes les classes, inclues les interfaces avec la regle de
disjonction pour l'heritage des pre-conditions et de
conjonction pour l'heritage des post-conditions. Donc en C++
il faudrait faire:



bool Derived::func_pre(bool assert) {
  bool contract = func_pre_cond || Base::func_pre(false);



  if (assert) assert( contract == true );



  return contract;
}



bool Derived::func_post(bool assert) {
  bool contract = func_post_cond && Base::func_post(false);



  if (assert) assert( contract == true );



  return contract;
}



Oui. Sauf que dans les faits, ça revient à dire que la classe
dérivée présente une interface différente que celle de la classe
de base. En Eiffel, par exemple, si je sais que j'ai en fait un
Derived, je peux programmer selon le contrat de la Derived, même
si ma référence dit Base. Le programmation par contrat en C++,
tel qu'il se pratique couramment, diffère de celle de l'Eiffel
en ce qu'elle exige toujours que les appels à travers
l'interface Base remplisse le contrat de Base. Ça a un effet
dans deux cas : d'abord, quand tu appelles la fonction depuis
une fonction membre d'une classe dérivée, et quand en tant que
client, tu sais que tu as réelement à faire avec une Derived, et
que tu veux profiter de la relaxation du contrat. On pourrait
facilement émuler Eiffel plus exactement dans le premier cas, en
rendant les fonctions virtuelles protected, à la place de
private ; en revanche, je ne suis pas convaincu que ce soit une
bonne idée. Quand au deuxième cas, si la classe dérivée veut
réelement offrir un contrat plus libérale, elle peut toujours le
faire, sans même les contraints de l'Eiffel. Et si le client
veut se servir du contrat du Derived, plutôt du contrat du Base,
il faut qu'il le dise explicitement, au moyen d'un dynamic_cast,
plutôt que ce que ça se fait tout seul. Là aussi, je trouve les
règles de C++ plus rigueureuses que celle de l'Eiffel. (En
somme, le C++ supporte mieux le programmation par contrat que
l'Eiffel. Mais il faut bien avouer que s'il le supporte, il ne
l'encourage pas particulièrement, et que ce n'est pas vraiment
entré dans les mœurs autant que dans Eiffel.)

Ca fait rapidement beaucoup de code! Donc je pars du principe
que chaque classe est responsable de ses donnees et c'est
tout.



Sauf que le contrat n'a rien à voir avec les données, et que
s'il va avoir une utilité quelconque, il faut bien qu'il soit
défini par l'interface.

Ce qui veut dire que si les appels virtuels se chaines, les
contrats aussi sous forme conjonctive (toujours). Ce n'est pas
l'ideal pour determiner la responsabilite du fautif, mais
c'est suffisant dans la plus part des cas. De toute facon, la
facon de Eiffel non plus n'est pas ideale pour determiner
correctement les responsabilites, voir:



R.B. Findler, M. Latendresse, and M. Felleisen.
"Behavioral Contracts and Behavioral Subtyping".
9th ACM Sigsoft International Symposium on Foundations
of Software Engineering, 2001



Donc je prefere favoriser la localite (cohesion) des contrats.



Donc, tu préfères les mettre dans la classe de l'interface,
c-à-d dans un seul endroit (localité garantie), avec une seule
copie du code (cohésion garantie).

> > > Et alors, est-ce que le problème n'est pas plutôt du à
> > > la virtualité en soi, qui empêche (en général) la
> > > génération



> > Je considere qu'un appel virtuel en presence d'heritage
> > simple comme etant l'unite a payer puisque je favorise la
> > flexibilite, donc le polymorphisme (sinon j'utiliserais
> > Fortan ou C).



> Oui, mais c'est elle qui introduit la plus grande différence
> dans les temps d'exécution.



Certe, la virtualite par rapport a l'inlining, c'est un
facteur 2-3 pour une fonction triviale.



Ça dépend de la contexte où elle apparaît. Ça peut être une
facteur 10, voire 100, dans certains cas. (L'intérêt de la
génération inline, ce n'est pas tellement l'économie de
l'appel ; c'est les possibilités supplémentaires d'optimisation
que ça revèle.)

Mais c'est le prix a paye pour la flexibilite du code
(reduction du couplage) et c'est inevitable.



Mais une fois que tu as fais cette décision, que l'héritage soit
virtuel ou non ne change plus grand chose.

Tandis que le cout introduit par l'heritage multiple
d'implementations pourrait etre optimise et ne pas ralentir la
virtualite dans le cas particulier de l'heritage multiple
d'interfaces (pas de donnees membres).



Si tu as besoin de l'héritage multiple d'implémentation, les
alternatifs que tu prends pour l'éviter risque d'être plus
chers.

Et en passant, j'ai lu l'article dont tu as posté le URL. Ce
n'est qu'une description de l'implémentation de l'héritage
virtuel dans g++. C-à-d du connu. Il n'explique en rien pourquoi
le Java n'a pas besoin de faire pareil pour son héritage
d'interface. Et les explications que j'ai pû trouver n'en parle
pas non plus ; elles expliquent très bien des implémentations
possibles de l'héritage dans des classes (ce qui ressemble
étroitement à ce que fait le C++, sauf pour une indirection de
plus), mais il ne parle pas comment fait la JVM avec l'appel
d'une fonction d'interface. Jusqu'ici, j'avoue ne pas voir
comment elle peut faire réelement différente que ne fait le C++
pour l'appel à travers une base virtuelle.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
6 7 8 9 10