OVH Cloud OVH Cloud

Quel tutoriel pour débuter ?

202 réponses
Avatar
Migrec
Bonjour,

Je cherche un tutoriel imprimable sur le web pour apprendre le C avant
d'acheter un bouquin "de référence".
Je suis débutant en programmation mais je connais les scripts shell
(linux), le HTML ou encore un peu de PHP.

Quel tutoriel imprimable me conseillez-vous pour débuter ? Les 560 pages
de celui du site du zéro
(http://www.siteduzero.com/tuto-29-8-0-apprenez-a-programmer-en-c.html)
découragent un peu mon imprimante, même avec le recto-verso
automatique... Mais le style me plaît ! Existe-t-il quelque chose dans
le même esprit mais en plus condensé ?

--
Migrec

10 réponses

Avatar
espie
In article ,
Jean-Marc Bourguet wrote:
Autre fantasme, le choix des langages est parfaitement rationnel et
purement technique... je parie que les criteres non-rationnels ou
non-technique (quand ils ne sont pas a la fois non-rationnels et
non-techniques) dominent. Naturellement, on peut les rationnaliser
techniquement apres coup, mais rationnaliser une autre decision serait tout
aussi facile.



Bof, sur un projet `recent', le choix du langage etait parfaitement lucide
et rationnel. J'ai reecrit un outil de C en perl.
Les raisons:
- le code de depart etait tres buggue vis-a-vis de certaines contraintes
(gestion correcte de chaine de caracteres)
- avoir une gestion simple de chaines et de regexp etait un plus appreciable
- j'avais un eventail de langages restreint sous la main (necessite d'etre
dans le systeme de base d'OpenBSD)
- perl est un langage que je connais bien.

Apres coup, le resultat est excellent. Comme attendu, moins de bugs que la
version C (nettement) et un outil bien plus rapide (meilleurs algos, et
performance dominee par les entrees-sorties de toutes facons). Le seul
gros defaut qu'on a trouve, c'est que c'est du code perl assez avance, et
que peu de monde maitrise l'OO en perl...
Avatar
Wykaaa
Marc Espie a écrit :
In article <48bbfa8b$0$927$,
Wykaaa wrote:
Oh là. Tu y vas fort quand même.
Si on prend le modèle COCOMO d'estimation des HM en fonction du nombre
de lignes de code estimées et des différents facteurs de coût, on voit
que les formules ne sont pas linéaires, justement parce que, par
exemple, 6 * 20 HM ne sont pas équivalents à 12 * 10 HM ce que montre,
justement, Frederick Brooks dans son livre "The mythical Man-Month".



Comme tu peux t'en douter, je suis contre la mecanisation a outrance
du travail en informatique, une forme de taylorisme de plus.

Il y a deux sortes de projets en informatique:
- les trucs triviaux, que tu vas filer a un pisseur de code java/C#/vbasic,
et qui va quand meme arriver a te le planter parce qu'il ne sait pas lire.
- les trucs interessants, qui sont presque infaisables.



C'est quand même assez pessimiste comme vision ?

Il me semble qu'il existe quand même des projets intéressants, non
triviaux et faisables.
J'ai eu la chance de travailler dans le domaine des compilateurs qui me
semble tomber dans la catégorie que j'indique.

Le premier cas se mecanise et se deshumanise tres bien. Appliquer des
techniques de management un peu cretines au 2e cas ne fonctionne pas trop...

Evidemment, le premier cas correspond a environ 90% du chiffre d'affaires
actuel de l'informatique... je ne sais pas trop s'il faut s'en rejouir ou
en pleurer, mais perso, je prefere faire des trucs qui appartiennent a la
2e categorie.



S'il n'y a que ces deux catégorie, je suis d'accord avec toi :-)
Avatar
Wykaaa
Marc Espie a écrit :
In article ,
Jean-Marc Bourguet wrote:
Autre fantasme, le choix des langages est parfaitement rationnel et
purement technique... je parie que les criteres non-rationnels ou
non-technique (quand ils ne sont pas a la fois non-rationnels et
non-techniques) dominent. Naturellement, on peut les rationnaliser
techniquement apres coup, mais rationnaliser une autre decision serait tout
aussi facile.



Bof, sur un projet `recent', le choix du langage etait parfaitement lucide
et rationnel. J'ai reecrit un outil de C en perl.
Les raisons:
- le code de depart etait tres buggue vis-a-vis de certaines contraintes
(gestion correcte de chaine de caracteres)
- avoir une gestion simple de chaines et de regexp etait un plus appreciable
- j'avais un eventail de langages restreint sous la main (necessite d'etre
dans le systeme de base d'OpenBSD)
- perl est un langage que je connais bien.

Apres coup, le resultat est excellent. Comme attendu, moins de bugs que la
version C (nettement) et un outil bien plus rapide (meilleurs algos, et
performance dominee par les entrees-sorties de toutes facons). Le seul
gros defaut qu'on a trouve, c'est que c'est du code perl assez avance, et
que peu de monde maitrise l'OO en perl...



Peu de monde maîtrise réellement l'OO de toute façon, hélas...
Avatar
espie
In article <48bc0673$0$949$,
Wykaaa wrote:

Peu de monde maîtrise réellement l'OO de toute façon, hélas...



Je conseille tres fortement le bouquin
_Refactoring: improving the design of existing code_, de Martin Fowler
(ISBN 0-201-48567-2). C'est sans doute le bouquin qui m'a fait faire
le plus de progres en qualite de code OO.

Ca, et d'aller fureter un peu dans une implementation smalltalk decente,
squeak, pour ne pas la citer...
Avatar
Antoine Leca
En news:48bbed5f$0$15612$, candide va escriure:
"crise du logiciel", il cite des projets d'ampleurs croissantes (j'ai
pas compris ses unités hommes/ mois) :



Jour×homme et mois×homme, voire année×homme, sont les unités standards
d'évaluation de la complexité des projets en phase de planification, elles
correspondent à l'assignation d'un programmeur pendant un jour ou mois à un
projet donné.
En anglais je crois qu'ils utilisent man-day (?) et man-month, donc l'auteur
a dû faire une traduction trop directe...

D'autres ont essayé de tordre le concept jusqu'à en faire une unité de
_suivi_ de planification, ce qui joint aux connaissances [litote] que l'on a
en génie logiciel, en particulier au niveau de l'évaluation de la complexité
ou de la replanification, a provoqué quelques jolis ratages.
Par ailleurs ce concept oublie complètement le coût de la communication
entre plusieurs personnes (« gestion de projet »), ce qui provoque aussi de
larges différences.


Compilateur (C, Pascal...) -> 120 HM
Compilateur ADA -> 1800 HM
Logiciel Navette -> 12000 HM
Système d'exploitation -> 60000 HM



C'est rigolo : il y a quelques minutes, j'étais sur la page [en anglais]
d'Andrew Tannenbaum, en particulier http://www.cs.vu.nl/~ast/brown/ qui
traite d'une discussion survenue il y a quelques années à propos de la
prétendue impossibilité pour L. Torvalds d'avoir écrit Linux ; et l'avis du
Pr. Tannenbaum¹ ne rejoint pas _du_tout_ les chiffres ci-dessus, tout au
plus on va trouver un rapport de 1 à 10 entre les extrêmes. Et en ce qui
concerne les valeurs elles-mêmes, il donne plutôt 12 pour le compilateur, et
75 (3 personnes pendant 2 années donc 25 mois) pour le système
d'exploitation, en l'occurence Coherent.


Bon, justement, un des logiciels les plus complexes est un OS. Or, des
OS comme Linux



Linux est un noyau. Le système d'exploitation que tu vois intègre pas mal
d'autre chose, des choses qui existaient avant² et d'autres nouvelles, et
une proportion non négligeable de ces choses nouvelles est écrite en C++.
Idem pour Windows (en fait encore plus de C++).

ou Windows pour x86 sont en général écrit majoritairement en C avec
de l'assembleur dans de petites proportions.

Donc, ma question est : pourquoi ne réécrit-on from scratch pas les OS
dans un langage objet genre C++ ?



Pourquoi faire ?
Il y a une dizaine d'années, les Bell Labs ont sorti un nouveau système,
Plan 9, que beaucoup ont vu comme un vrai successeur de Unix. Pourtant, il
n'a pas eu de suite pour les utilisateurs, et on pense généralement que
c'est parce qu'il n'apportait pas assez de choses importantes pour valoir la
peine de changer.

Si Microsoft a écrit à la fin des années 80 en partant de « rien » un
nouveau système d'exploitation, c'est uniquement parce qu'ils voulaient en
avoir un en propre.

Et par ailleurs, j'ai bien l'impression (mais je ne peux pas le montrer)
qu'au début de cette décennie, lorsqu'on écrivait un nouveau système
d'exploitation en partant de rien, on l'écrivait généralement en C++.

De plus, la première utilisation de C++ a été si je ne m'abuse pour écrire
un système d'exploitation (chez Bell ou AT&T).


Autre question dans le même registre : comment se fait-il que les
implémentations de langage objets comme Python et Ruby soient écrits
en C et non pas en C++ ?



La portabilité (au moment où ils ont été conçus).
Une bibliothèque écrite en C était directement et largement portable.
Si elle était écrite en C++, cela semble moins évident, soit parce que la
cible n'a pas de compilateur C++ au même niveau, soit parce que cela
entraîne trop de composants liés qui font que tu ne peux plus extraire une
bibliothèque de l'ensemble du système, tu es obligé de porter l'ensemble...
Et les règles de nommage et autres ABI (toutes incompatibles) des différents
compilateurs/lieurs C++ obligaient à des contortions qui sont une véritable
gêne pour des environnements dont un point important³ est la possibilité de
realiser dynamiquement des extensions au système.


Antoine
__________
Notes :
¹: Andrew Tannenbaum sait de quoi il parle, il effectivement dirigé
l'écriture d'un compilateur C d'une part, et d'un système d'exploitation
d'autre part.
² /Linux [intègre] des choses qui existaient avant/ : comme les outils GNU ;
d'ailleurs, la motivation initiale de L. Torvalds était d'avoir un noyau
pour pouvoir utiliser bash et compagnie.
³: en particulier pour les performances.
Avatar
Wykaaa
Marc Espie a écrit :
In article <48bc0673$0$949$,
Wykaaa wrote:

Peu de monde maîtrise réellement l'OO de toute façon, hélas...



Je conseille tres fortement le bouquin
_Refactoring: improving the design of existing code_, de Martin Fowler
(ISBN 0-201-48567-2). C'est sans doute le bouquin qui m'a fait faire
le plus de progres en qualite de code OO.

Ca, et d'aller fureter un peu dans une implementation smalltalk decente,
squeak, pour ne pas la citer...



Effectivement c'est un bon bouquin.

Moi, je disposais en 97 des sources de Cyberdog (Apple).

La plupart des composants étaient codés en C++ (eh oui !). A cette
époque, je donnais beaucoup de cours C++ (en moyenne 1 cours de 5 jours
chaque mois) et chaque portion de code de Cyberdog pouvait servir
d'exemple de bonne programmation en C++.

Ce qu'est Cyberdog :
Cyberdog est un ensemble de composants OpenDoc dédiés à l'Internet ou
l'intranet. Ces composants permettent: de naviguer sur le Web, recevoir
et émettre des mails, lire des articles des News, naviguer sur les zones
et les serveur AppleTalk, échanger des fichiers par FTP, se connecter
par Telnet. Cyberdog offre une forte intégration entre ces composants et
les autres applications OpenDoc.
Avatar
Wykaaa
candide a écrit :
Francois a écrit :

Tiens, en tapant "Conception et programmation orientée objet" sur
google, je tombe sur ce cours : http://rainet.enic.fr/unit/A43/index.htm
Ça a l'air vachement bien. :-)



Je trouve ce lien très intéressant et l'auteur fait un bel effort de
vulgarisation, dommage que tout soit dans des frames.

Alors, dans sa séquence 1, l'auteur attribue le développement des
langages OO à la "crise du logiciel" (qui proviendrait de sa
complexification croissante) et justement dans la 4ème page du menu
"crise du logiciel", il cite des projets d'ampleurs croissantes (j'ai
pas compris ses unités hommes/ mois) :

Compilateur (C, Pascal...) -> 120 HM
Compilateur ADA -> 1800 HM
Logiciel Navette -> 12000 HM
Système d'exploitation -> 60000 HM

Bon, justement, un des logiciels les plus complexes est un OS. Or, des
OS comme Linux ou Windows pour x86 sont en général écrit majoritairement
en C avec de l'assembleur dans de petites proportions.

Donc, ma question est : pourquoi ne réécrit-on from scratch pas les OS
dans un langage objet genre C++ ? Trop de travail ? pas utile ?



ChorusOS (système d'exploitation temps réel micronoyau conçu pour les
systèmes embarqués) était écrit à 95% en C++.
Je connaissais très bien les gens de Chorus avant qu'ils soient rachetés
par Sun.

Autre question dans le même registre : comment se fait-il que les
implémentations de langage objets comme Python et Ruby soient écrits en
C et non pas en C++ ? Pour Python 3000, il semblerait qu'il avait été
envisagé de tout réécrire, cf.

http://python.org/dev/peps/pep-3000/

Et les jvm, elles sont écrites en C ou en C++ ? Un compilateur C++ est
écrit en quoi ? Sinon, il me semble que le compilateur DMD est écrit en C.





Avatar
Antoine Leca
En news:48bbe7f6$0$857$, Wykaaa va escriure:
Antoine Leca a écrit :
En news:48ba96bf$0$961$, Wykaaa va escriure:
Donc, pour résumer, l'approche objet s'appuie sur 4 concepts majeurs
de base :
- le concept d'abstraction (se matérialise par la notion de classe
dans un langage de programmation objet)

- le concept d'encapsulation qui permet à un objet (une classe donc)
de ne montrer à l'extérieur que ce qui est nécessaire à son
utilisation. Ce concept est matérialisé dans les LOO (langages
orientés objet) par les directives de contrôle d'accès (private,
protected, public)

- le concept de modularité (au sens objet) qui s'ppuie sur 2
principes :
- La forte cohésion : un objet ne traite que d'une seule
abstraction et il la traite entièrement
- Le faible couplage : minimisation des dépendances entre
classes et encapsulation
ATTENTION : cette notion de modularité est piégeuse car dans les
langages on a aussi la notion de package (regroupement de plusieurs
classes). Le faible couplage est souvent mis en oeuvre, dans les
grosses applications, au niveau des packages et non au niveau des
classes car la granularité est trop fine.

- Enfin le concept de hiérarchie. Il est mis en oeuvre, dans les
LOO, à travers l'héritage mais aussi à travers la
composition/agrégation d'objets.



Si on revient à C, le concept d'encapsulation est me semble-t-il
traité par la séparation .h/.c et des règles de gestion, et le
concept de modularité par d'autres règles de gestion, mais le
langage n'est aucunement un obstacle.



C'est la modularité qui est traitée par la séparation .h/.c.
Ensuite, tout dépend de ce que tu mets dans le .h.



En fait, cela dépend surtout des règles de gestion que tu [t'] imposes.
Si tu imposes comme règle de gestion : n'utiliser des « objets » que ce qui
est documenté, pas ce qui est écrit dans le .h --c'est la règle retenue pour
le « C portable » promu par la norme-- tu obtiens un certain niveau.
Si par contre tu dis « la documentation ultime est le source », tu obtiens
un autre niveau.

Quant à savoir si ces règles sont des niveaux d'abstraction, d'encapsulation
ou de modularité, c'était ma question.


Pour respecter le concept d'encapsulation, le .h présentera uniquement
un pointeur sur la structure [...]



Ah OK, je me suis mal exprimé.
Toi tu fais allusion au .h externe, celui qui est #inclus par les
consomateurs de l'objet (.h que tu vois unique par objet).
Personnellement, je faisais référence aux véritables .h situés au même
niveau que les .c, avec la vraie structure des « objets ».
Le préprocesseur C tel que normalisé a d'ailleurs un mécanisme prêt pour
cela, les #include avec <totatu.h> ou "objet_toto.h".

Par ailleurs, une telle organisation facilite ÀMHA énormément
l'implémentation de l'héritage (simple).


[ Pour respecter le concept d'encapsulation, le .h présentera uniquement
...]
un moyen d'accès aux pointeurs sur
fonctions qui représentent les "méthodes" de l'abstraction



Nous y voilà : tu as introduit ces fameux pointeurs vers fonction.
Mais pourquoi les méthodes doivent-elles être des pointeurs vers fonction ?

C'est bien beau d'encapsuler, mais pourquoi rajouter un niveau
d'indirection, par ailleurs tellement coûteux que certaines implémentations
permettent explicitement de le contourner ? voire comme C++ le font par
défaut ?


(Il faut que le programmeur puisse les appeler. C'est un moyen de
rendre les fonctions "publiques").



On peut aussi appeler des fonctions tout court, le fait que ce soit des
pointeurs me paraît être du sucre théorique, si tu vois ce que je veux dire.


Pour en revenir à l'encapsulation telle que définie ci-dessus, les marqueurs
public/protected/private de C++ permettent effectivement (et jusqu'à un
certain point) de diriger l'encapsulation, mais je crois qu'à ce niveau il y
a un équilibre à trouver, équilibre qui peut changer en fonction de la
maturité du projet : et je ne vois pas cet aspect dynamique dans les
concepts ci-dessus.


Le concept d'héritage est (toujours « me semble-t-il »)
spécifiquement mis en ouvre par les règles relatives aux pointeurs
de structure et d'union.



Pour l'héritage, on peut faire comme tu dis ou mettre en place tout un
mécanisme à base de macros du préprocesseur (ce qui était fait pour
X-Window/Motif).



Qui je suppose va finir par utiliser la même règle sous-jacente de
« commutatibilité » des structures...

De toutes manières, en C, quand on définit une union, on se retrouve le plus
souvent à mettre en place des macros du préprocesseur, au moins pour cacher
les traînées de sélecteurs sans signification...


Le plus difficile est de simuler le polymorphisme car il faut tout
faire "à la main" (se le programmer...).



Euh, il faut aussi de l'héritage polymorphique pour avoir une « vraie »
approche objet ? Là encore, quel est le concept qui veut cela ? (ne me dis
pas hiérarchie, parce que pour moi, hiérarchie signifie exactement le
contraire de ce que permet l'héritage polymorphique...)
Tu n'es pas en train « d'étirer » un peu le modèle, là ?



Le concept d'abstraction tel que décrit ci-dessus n'a aucun sens
pour moi. Se reférerait-il à l'utilisation de pointeurs ?



En C, une abstraction sera représentée par une structure qui contient
les attributs de l'abstraction (les champs de données) et les
pointeurs sur les fonctions qui permettent de manipuler ces champs
de données (l'équivalent des méthodes dans une classe "à la C++")



Si je comprend bien, le concept d'abstraction consiste à regrouper les «
attributs » et les « méthodes » permettant de les manipuler, c'est bien cela
?
Donc en C, on va effectivement utiliser des structures (ce que j'avais
sous-entendu dès le début).
Pour ce qui est des méthodes, le plus courant en C est d'utiliser des règles
de nommage.

Maintenant, je ne comprends pas la démonstration de la nécessité de ranger
toutes les méthodes comme des pointeurs vers fonctions logées dans chaque
instance des objets manipulés ; d'ailleurs je pense que la plupart des
implémentations de C++ ne font pas cela, et utilisent un niveau
supplémentaire comme par exemple une table de méthodes virtuelles ; d'autres
ont des informations dynamiques de type qui jouent un rôle similaire.


Si j'ai bien compris, on peut faire ce genre d'approche orientée
objets avec le langage C plus quelques règles (communément
observées) sur ce qui s'appelle traditionellement en C la
compilation séparée, c'est-à-dire le découpage en module ?



Absolument. c'est ce que j'ai essayé de décrire ci-dessus



Et, pour autant que j'ai bien suivi, pour peu que l'on ait des structures,
des pointeurs (même les /access/ d'Ada83 devraient suffire), un mécanisme de
compilation séparée et la possibilité de définir quelques règles de gestion,
on doit pouvoir le faire avec beaucoup de L3G, non ?
(OK, on s'accorde généralement à considérer qu'il faut aussi des pointeurs
vers fonctions mais je n'ai toujours pas compris pourquoi.)


Donc, si tu peux utiliser un langage qui n'est *pas* « orienté objet » pour
utiliser « l'approche objet », cela signifie que les deux concepts ne sont
pas sur le même plan, n'est-ce pas ?


Le vrai problème, c'est qu'il devient alors facile de rendre complètement
floue la définition de « l'approche objet » (d'où les inutiles querelles de
clochers déjà mentionnées, et même vues récemment), à partir du moment où on
perd la relation supposée existente entre cette approche et les langages
(dits) orientés objet.
À ce s'ajoute bien sûr que ces dits langages n'oblige que très
exceptionnellement à se contraindre strictement à cette approche, à la fois
pour des raisons pratiques (le faire de contrainte le style des programmeurs
de manière trop catégorique serait s'interdire d'entrée la possibilité de
succès) comme de portabilité (quoi qu'on en dise, il est important de
/pouvoir/ capitaliser sur l'existant des programmes et autres bibliothèques
non-orientés objet).


Résultat, tu te retrouves avec des références de 1200 pages, où tu peux
certainement trouver à boire et à manger.

Je comprends tout-à-fait les interrogations de François...


Antoine
Avatar
Wykaaa
Antoine Leca a écrit :
En news:48bbe7f6$0$857$, Wykaaa va escriure:
Antoine Leca a écrit :
En news:48ba96bf$0$961$, Wykaaa va escriure:
Donc, pour résumer, l'approche objet s'appuie sur 4 concepts majeurs
de base :
- le concept d'abstraction (se matérialise par la notion de classe
dans un langage de programmation objet)

- le concept d'encapsulation qui permet à un objet (une classe donc)
de ne montrer à l'extérieur que ce qui est nécessaire à son
utilisation. Ce concept est matérialisé dans les LOO (langages
orientés objet) par les directives de contrôle d'accès (private,
protected, public)

- le concept de modularité (au sens objet) qui s'ppuie sur 2
principes :
- La forte cohésion : un objet ne traite que d'une seule
abstraction et il la traite entièrement
- Le faible couplage : minimisation des dépendances entre
classes et encapsulation
ATTENTION : cette notion de modularité est piégeuse car dans les
langages on a aussi la notion de package (regroupement de plusieurs
classes). Le faible couplage est souvent mis en oeuvre, dans les
grosses applications, au niveau des packages et non au niveau des
classes car la granularité est trop fine.

- Enfin le concept de hiérarchie. Il est mis en oeuvre, dans les
LOO, à travers l'héritage mais aussi à travers la
composition/agrégation d'objets.


Si on revient à C, le concept d'encapsulation est me semble-t-il
traité par la séparation .h/.c et des règles de gestion, et le
concept de modularité par d'autres règles de gestion, mais le
langage n'est aucunement un obstacle.


C'est la modularité qui est traitée par la séparation .h/.c.
Ensuite, tout dépend de ce que tu mets dans le .h.



En fait, cela dépend surtout des règles de gestion que tu [t'] imposes.
Si tu imposes comme règle de gestion : n'utiliser des « objets » que ce qui
est documenté, pas ce qui est écrit dans le .h --c'est la règle retenue pour
le « C portable » promu par la norme-- tu obtiens un certain niveau.
Si par contre tu dis « la documentation ultime est le source », tu obtiens
un autre niveau.



Tout à fait. Perso, je préfère que la doc ultime soit le source.

Quant à savoir si ces règles sont des niveaux d'abstraction, d'encapsulation
ou de modularité, c'était ma question.


Pour respecter le concept d'encapsulation, le .h présentera uniquement
un pointeur sur la structure [...]



Ah OK, je me suis mal exprimé.
Toi tu fais allusion au .h externe, celui qui est #inclus par les
consomateurs de l'objet (.h que tu vois unique par objet).
Personnellement, je faisais référence aux véritables .h situés au même
niveau que les .c, avec la vraie structure des « objets ».
Le préprocesseur C tel que normalisé a d'ailleurs un mécanisme prêt pour
cela, les #include avec <totatu.h> ou "objet_toto.h".



Enfin cette distinction établit une priorité dans les règles de
recherche des librairies.

Par ailleurs, une telle organisation facilite ÀMHA énormément
l'implémentation de l'héritage (simple).



A cause des priorités ?


[ Pour respecter le concept d'encapsulation, le .h présentera uniquement
...]
un moyen d'accès aux pointeurs sur
fonctions qui représentent les "méthodes" de l'abstraction



Nous y voilà : tu as introduit ces fameux pointeurs vers fonction.
Mais pourquoi les méthodes doivent-elles être des pointeurs vers fonction ?



Pour avoir un nom unique pour l'abstraction qui regroupe ainsi les
attributs et les méthodes (du moins les pointeurs sur...)

C'est bien beau d'encapsuler, mais pourquoi rajouter un niveau
d'indirection, par ailleurs tellement coûteux que certaines implémentations
permettent explicitement de le contourner ? voire comme C++ le font par
défaut ?



Enfin, ce n'est qu'une indirection et les compilateurs peuvent faire des
optimisation en fonction du "packaging " des modules.


(Il faut que le programmeur puisse les appeler. C'est un moyen de
rendre les fonctions "publiques").



On peut aussi appeler des fonctions tout court, le fait que ce soit des
pointeurs me paraît être du sucre théorique, si tu vois ce que je veux dire.



les fonctions doivent faire partie intégrante de l'abstraction sinon on
ne peut pas garantir que ce sont bien CES fonctions qui manipulent CETTE
structure.


Pour en revenir à l'encapsulation telle que définie ci-dessus, les marqueurs
public/protected/private de C++ permettent effectivement (et jusqu'à un
certain point) de diriger l'encapsulation, mais je crois qu'à ce niveau il y
a un équilibre à trouver, équilibre qui peut changer en fonction de la
maturité du projet : et je ne vois pas cet aspect dynamique dans les
concepts ci-dessus.



Je ne comprends pas ce que tu veux dire.
Par exemple, pour certains (en C++), les attributs doivent être
systématiquement privés (même protected est interdit).
C'est d'ailleurs le cas en SmallTalk où on n'a pas le choix : les
attributs sont privés et les méthodes sont publiques.


Le concept d'héritage est (toujours « me semble-t-il »)
spécifiquement mis en ouvre par les règles relatives aux pointeurs
de structure et d'union.


Pour l'héritage, on peut faire comme tu dis ou mettre en place tout un
mécanisme à base de macros du préprocesseur (ce qui était fait pour
X-Window/Motif).



Qui je suppose va finir par utiliser la même règle sous-jacente de
« commutatibilité » des structures...



Oui,en quelque sorte.

De toutes manières, en C, quand on définit une union, on se retrouve le plus
souvent à mettre en place des macros du préprocesseur, au moins pour cacher
les traînées de sélecteurs sans signification...



C'est plus propre en effet.


Le plus difficile est de simuler le polymorphisme car il faut tout
faire "à la main" (se le programmer...).



Euh, il faut aussi de l'héritage polymorphique pour avoir une « vraie »
approche objet ? Là encore, quel est le concept qui veut cela ? (ne me dis
pas hiérarchie, parce que pour moi, hiérarchie signifie exactement le
contraire de ce que permet l'héritage polymorphique...)
Tu n'es pas en train « d'étirer » un peu le modèle, là ?



Non. Le vrai apport de la programmation objet est le polymorphisme qui
permet une véritable évolutivité des logiciel.
Je dis souvent que "la chasse aux switch est à la programmation objet ce
que la chasse aux goto est à la programmation structurée"

EN objet, par exemple, je n'utilise pas les enum, je fais une
sous-classe par valeur de l'enum et j'utilise le polymorphisme.



Le concept d'abstraction tel que décrit ci-dessus n'a aucun sens
pour moi. Se reférerait-il à l'utilisation de pointeurs ?


En C, une abstraction sera représentée par une structure qui contient
les attributs de l'abstraction (les champs de données) et les
pointeurs sur les fonctions qui permettent de manipuler ces champs
de données (l'équivalent des méthodes dans une classe "à la C++")



Si je comprend bien, le concept d'abstraction consiste à regrouper les «
attributs » et les « méthodes » permettant de les manipuler, c'est bien cela
?



Oui car une abstraction est définie par ses propriétés (ses attributs),
son comportement (les méthodes, ou plutôt les algos des méthodes) et une
identité (soit un identifiant, soit une adresse mémoire, soit une
adresse disque pour les objets persistants)

Donc en C, on va effectivement utiliser des structures (ce que j'avais
sous-entendu dès le début).
Pour ce qui est des méthodes, le plus courant en C est d'utiliser des règles
de nommage.



J'espère que pour les abstractions (les struct), le manuel des règles de
programmation définit également des règles de nommage !

Maintenant, je ne comprends pas la démonstration de la nécessité de ranger
toutes les méthodes comme des pointeurs vers fonctions logées dans chaque
instance des objets manipulés ; d'ailleurs je pense que la plupart des
implémentations de C++ ne font pas cela, et utilisent un niveau
supplémentaire comme par exemple une table de méthodes virtuelles ; d'autres
ont des informations dynamiques de type qui jouent un rôle similaire.



Justement, en C++, un appel de méthode virtuelle se traduit par :
- passage par un pointeur sur la table des fonctions virtuelles (donc
une indirection). Ce pointeur figure dans chaque instance.
- une indexation dans cette table sur la "bonne" fonction
- une indirection sur la méthode puisque la table des fonctions
virtuelles contient uniquement les pointeurs sur les méthodes (il y a
donc 2 indirections et une indexation)

Lorsque les méthodes virtuelles sont peu nombreuses, la table figure
dans chaque instance pour éviter une indirection.

Ce que je décris ci-dessus est une implémenttion classique des fonctions
virtuelles en C++. Bien sûr, des astuces plus "siouk" peuvent être
envisagées dans certains cas.


Si j'ai bien compris, on peut faire ce genre d'approche orientée
objets avec le langage C plus quelques règles (communément
observées) sur ce qui s'appelle traditionellement en C la
compilation séparée, c'est-à-dire le découpage en module ?


Absolument. c'est ce que j'ai essayé de décrire ci-dessus



Et, pour autant que j'ai bien suivi, pour peu que l'on ait des structures,
des pointeurs (même les /access/ d'Ada83 devraient suffire), un mécanisme de
compilation séparée et la possibilité de définir quelques règles de gestion,
on doit pouvoir le faire avec beaucoup de L3G, non ?
(OK, on s'accorde généralement à considérer qu'il faut aussi des pointeurs
vers fonctions mais je n'ai toujours pas compris pourquoi.)



Les pointeurs sur fonctions permettent de mettre en oeuvre à la fois les
concepts d'abstraction, d'encapsulation et même de modularité (car cela
résoud certains problèmes de contexte dans le cadre de la portabilité
des modules). Ceci serait trop long à développer ici.

Bien sur qu'on peut le faire avec des L3G qui possèdent les
caractéristiques que tu indique mais au prix de beaucoup d'effort et
d'une discipline de fer de la part des développeurs.


Donc, si tu peux utiliser un langage qui n'est *pas* « orienté objet » pour
utiliser « l'approche objet », cela signifie que les deux concepts ne sont
pas sur le même plan, n'est-ce pas ?


Le vrai problème, c'est qu'il devient alors facile de rendre complètement
floue la définition de « l'approche objet » (d'où les inutiles querelles de
clochers déjà mentionnées, et même vues récemment), à partir du moment où on
perd la relation supposée existente entre cette approche et les langages
(dits) orientés objet.



L'approche objet, pour moi, ce sont les 4 concepts majeurs exprimés par
Grady Booch il y a plus de 20 ans maintenant.
On peut trouver cela rétrograde, pas novateur, etc. mais je te garantis
que si tu comprends ces 4 concepts (profondément), si tu les appliques
rigoureusement de concert et si tu as un langage qui permet de les
mettre en oeuvre aisément, tu fais des miracles sur la clarté du code,
son évolutivité, sa maintenabilité et même sa fiabilité.
Mais ceci exige une grande discipline que beaucoup refuse ou même ne
comprenne pas. C'est pour ça que j'ai dit, par ailleurs dans ce fil, que
peu de personnes comprennent REELLEMENT l'approche objet ou du moins ne
sont pas prêts à faire les efforts nécessaires...

Ce que je veux dire c'est, oui bien sûr, on peut essayer de découpler
les concepts qui sous-tendent le paradigme objet des langages qui les
mettre en oeuvre mais c'est une erreur de vouloir le faire car cela
augmente l'effort et la discipline requis pour les développeur qui n'ont
pas besoin de ça car c'est déjà difficile si on ne découple pas les
concepts et les outils adéquats qui les mettent en oeuvre.

À ce s'ajoute bien sûr que ces dits langages n'oblige que très
exceptionnellement à se contraindre strictement à cette approche, à la fois
pour des raisons pratiques (le faire de contrainte le style des programmeurs
de manière trop catégorique serait s'interdire d'entrée la possibilité de
succès)


Je ne suis pas du tout d'accord avec ça.. Contraindre strictement à
cette approche est, au contraire, une meilleure garantie de succès et
j'en ai de nombreux exemples.
J'ai audité dernièrement, avec l'outil Logiscope, une application Java
de plus de 450 000 lignes de code.
Les règles de conception et de programmation du projet étaient
drastiques. Pour te donner une idée, aucune méthode ne devait faire plus
de 20 lignes de code, le nombre moyen de méthodes par classe ne devait
pas dépasser 5 pour garantir une modularité très forte (il y avait plus
de 9 000 classes !) et tout à l'avenant.

L'audit a montré que ces contraintes étaient respectées, pourtant la
phase de programmation n'a duré que 9 mois avec 45 développeurs.

L'application était un succès total.

comme de portabilité (quoi qu'on en dise, il est important de
/pouvoir/ capitaliser sur l'existant des programmes et autres bibliothèques
non-orientés objet).



La portabilité est un problème différent de la réutilisation de code
existant, objet ou non.


Résultat, tu te retrouves avec des références de 1200 pages, où tu peux
certainement trouver à boire et à manger.

Je comprends tout-à-fait les interrogations de François...



Mon sentiment c'est que sur ces sujets, si l'on n'a pas pratiqué ou
examiné de grands ou très grands projets qui appliquent strictement
l'approche objet, on ne peut pas comprendre réellement la raison de tous
ces concepts et principes ni pourquoi leur application stricte
(j'insiste !) dans un projet de grande envergure est donne une meilleure
chance de succès.
Avatar
Francois
Encore une question de béotien, parque pour l'instant c'est très
théorique tout ça. :-)

Pourrais-je avoir s'il vous plaît un exemple de code *en C* où l'on a
l'instanciation d'un objet "compteur" ... d'une manière orienté objet donc ?

Par exemple, on pourrait imaginer que l'objet compteur :
- possède un attribut de donnée interne x qui est la valeur du compteur
valant 0 au départ juste après instanciation
- possède une méthode-propriété qui affiche la valeur de x
- possède une méthode-action qui incrémente la valeur de x
- possède une méthode-action qui initialise à 0 la valeur de x

J'aimerais bien voir la tête que ça peut avoir un exemple simple comme
celui-là en C.

Merci d'avance.

--
François