This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
cpp:syntax:class:deriving [2019/09/24 06:20] 127.0.0.1 external edit |
cpp:syntax:class:deriving [2022/11/18 10:47] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== La classe dérivée ====== | + | ====== Classe dérivée & héritage ====== |
- | La classe dérivée est une **extension** d'une classe de base. C'est une classe à laquelle on a ajouté des __champs__ et des __fonctions membres__ supplémentaires. | + | ===== Pourquoi hériter d'une classe ==== |
- | La classe dérivée **hérite** de l'ensemble des __fonctions membres__ et des __champs__ définis par la ou les classes de bases. C'est pour cela que l'on parle d'héritage quand on parle de classes dérivées d'une autre classe. | + | Supposons que nous avons réaliser un objet qui effectuer une certaine tâche de base. |
+ | <code cpp> | ||
+ | class enumerate_characters | ||
+ | { | ||
+ | private: | ||
+ | int mPosition; | ||
+ | int mNumberOfCharacters; | ||
+ | const char* mString; | ||
+ | public: | ||
+ | enumerate_characters(const char* aString, int theNumberOfCharacters): | ||
+ | mString(aString), mNumberOfCharacters(theNumberOfCharacters) {} | ||
+ | | ||
+ | int next() | ||
+ | { | ||
+ | return mPosition < mNumberOfCharacters ? | ||
+ | (int)mString[mPosition++] : -1; | ||
+ | } | ||
+ | void reset() | ||
+ | { | ||
+ | mPosition = 0; | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | Cette classe énumère les caractère de la chaîne de caractère passée en paramètre en commençant par le premier caractère et en terminant par le dernier caractère. | ||
- | ===== Déclaration d'une classe dérivée ==== | + | Supposant que je ne souhaite plus énumérer les caractères en commençant par le premier caractère et en terminant par le dernier caractère mais à l'inverse en commençant par le dernier caractère et en terminant par le premier. Je devrais ajouter à cette classe la fonction ''previous()'' par exemple. Je peux soit ajouter cette méthode dans la classe ou au contraire ajouter cette méthode à une extension de la classe, ce que l'on appelle une classe dérivée ou une classe fille qui hérite de son parent. L'intérêt est dans ce cas que je vais pouvoir modifier le comportement de la classe. |
- | La syntaxe pour déclarer une classe comme héritant d'une classe de base est la suivante : | ||
<code cpp> | <code cpp> | ||
- | + | class enumerate_characters | |
- | class DerivingClass: public BaseClass | + | |
{ | { | ||
- | }; | + | private: |
+ | int mPosition; | ||
+ | protected: | ||
+ | int mNumberOfCharacters; | ||
+ | const char* mString; | ||
+ | public: | ||
+ | enumerate_characters(const char* aString, int theNumberOfCharacters): | ||
+ | mString(aString), mNumberOfCharacters(theNumberOfCharacters) {} | ||
+ | |||
+ | int next() | ||
+ | { | ||
+ | return mPosition < mNumberOfCharacters ? | ||
+ | (int)mString[mPosition++] : -1; | ||
+ | } | ||
+ | void reset() | ||
+ | { | ||
+ | mPosition = 0; | ||
+ | } | ||
+ | } | ||
+ | class bidi_enumerate_characters: enumerate_characters | ||
+ | { | ||
+ | private: | ||
+ | int mReversePosition; | ||
+ | public: | ||
+ | bidi_enumerate_characters(const char* aString, int theNumberOfCharacters): | ||
+ | enumerate_characters(aString, theNumberOfCharacters), mReversePosition(theNumberOfCharacters) {} | ||
+ | | ||
+ | int previous() | ||
+ | { | ||
+ | return mReversePosition >= 0 ? | ||
+ | (int)mString[mPosition++] : -1; | ||
+ | } | ||
+ | void reset() | ||
+ | { | ||
+ | mReversePosition= mNumberOfCharacters; | ||
+ | enumerate_characters()::reset(); | ||
+ | } | ||
+ | } | ||
</code> | </code> | ||
- | ==== Accessibilité des méthodes d'une classe de base ==== | + | La classe ''bidi_enumerate_characters'' est une extension de la classe ''enumerate_characters'' : |
- | Cette définition indique que la classe ''DerivingClass'' hérite de la classe ''BaseClass'' et donc que l'ensemble des méthodes et des champs définis par la classe ''BaseClass'' sont présents dans la classe ''DerivingClass''. Cependant, il faut faire attention, présent ne veut pas dire qu'ils sont accessibles dans la classe de base. Ainsi, nous avons les règles d'accessibilité suivantes pour les méthodes et les champs définis dans la classe de base. | + | <code cpp> |
+ | class bidi_enumerate_characters: enumerate_characters | ||
+ | { | ||
+ | </code> | ||
- | ^ Déclaration dans la classe de base ^ Accesibilité dans la classe dérivée ^ | + | ce qui veut que l'ensemble des champs et méthodes présents dans la classe ''enumerate_characters'' sont présents dans la classe ''bidi_enumerate_characters''. Attention, les champs ou méthodes privées sont présentes mais ne sont pas accessibles. Ici, nous avons besoin des champs ''mNumberOfCharacters'' et ''mString'' dans la classe dérivée, c'est pour cela que nous avons modifié la classe de base ''enumerate_characters'' pour les rendre toujours inaccessibles en dehors de la classe mais accessible dans les classes derivées en les déclarant comme ''proctected'' : |
- | |les champs ou méthodes déclarés ''public'' dans la classe de base | les champs et méthodes sont accessibles dans la classe dérivée | | + | |
- | |les champs ou méthodes déclarés ''protected'' dans la classe de base | les champs et méthodes sont accessibles dans la classe dérivée | | + | |
- | |les champs ou méthodes déclarés ''private'' dans la classe de base | les champs et méthodes ne sont pas accessibles dans la classe dérivée | | + | |
- | Cependant, il faut aussi s'intéresser à l'accesibilité des méthodes en dehors de la classe dérivée. C'est ici qu'intervient les attributs d'accessibilité que l'on place devant le type de la classe dont hérite qui peuvent être ''public'', ''protected'' ou ''private''. | + | <code cpp> |
+ | class enumerate_characters | ||
+ | { | ||
+ | private: | ||
+ | int mPosition; | ||
+ | protected: | ||
+ | int mNumberOfCharacters; | ||
+ | const char* mString; | ||
+ | public: | ||
+ | </code> | ||
- | ^ Attribut d'héritage ^ Accessibilité en dehors de la classe ^ Accessibilité dans une classe dérivant de ''DerivingClass'' ^ | + | Nous voyons que la classe ''bidi_enumerate_characters'' ajoute la fonction ''previous()'' à la classe de base. |
- | |''DerivingClass: public BaseClass''| Les méthodes et champs ''public'' de ''BaseClass'' sont accessibles. | Les méthodes et champs ''public'' ou ''protected'' de ''BaseClass'' sont accessibles à la classe dérivée. | | + | |
- | |''DerivingClass: private BaseClass''| Aucune méthode ou champs de ''BaseClass'' ne sont accessibles. | Aucune méthode ou champs de ''BaseClass'' ne sont accessibles à la classe dérivée. | | + | |
- | |''DerivingClass: protected BaseClass''| Aucune méthode ou champs de ''BaseClass'' sont accessibles. | Les méthodes et champs ''public'' ou ''protected'' de ''BaseClass'' sont accessibles à la classe dérivée. | | + | |
+ | <code cpp> | ||
+ | int previous() | ||
+ | { | ||
+ | return mReversePosition >= 0 ? | ||
+ | (int)mString[mPosition++] : -1; | ||
+ | } | ||
+ | </code> | ||
- | ===== L'héritage multiple ==== | + | c'est pour cela qu'on dit qu'une classe dérivée est une extension de la classe de base par ce qu'elle peut ajouter des fonctions et des comportements. |
- | Il est possible d'hériter de plus d'une classe. Pour ce faire, il suffit de spécifier la liste des classes dont nous souhaitons hériter comme suit: | + | Ensuite, nous constatons que la classe ''bidi_enumerate_characters'' rédéfinit la méthode ''reset()'' : |
<code cpp> | <code cpp> | ||
+ | void reset() | ||
+ | { | ||
+ | mReversePosition= mNumberOfCharacters; | ||
+ | enumerate_characters()::reset(); | ||
+ | } | ||
+ | </code> | ||
- | class DerivingClass: | + | En effet, lorsque nous appellons la méthode ''reset()'', nous mettons d'abord le champs ''mReversePosition'' à ''mNumberOfCharacters'' et puis ensuite nous appellons la méthode ''reset()'' de la classe de base ''enumerate_characters()::reset()''. Nous avons spécialisé le comportement de la fonction ''reset()'' par rapport à la fonction ''reset()'' de la classe de base. C'est pour cela que les classes dérivées sont aussi appellées classes spécialisées. |
- | public BaseClass, | + | |
- | private ImplementationClass, | + | |
- | public AnotherBaseClass | + | |
- | { | + | |
- | }; | + | |
- | </code> | + | [[cpp:syntax:class:deriving:creating|Définir une classe dérivée]] |
+ | |||
+ | [[cpp:syntax:class:deriving:methods|Définir champs et méthodes dans une classe dérivée]] | ||
+ | |||
+ | [[cpp:syntax:class:deriving:constructor|Définir les constructeurs dans une classe dérivée]] | ||
- | Si l'héritage multiple est intellectuellement intéressant, rapidement nous pouvons avoir des problèmes de conflit entres les différentes fonctions membres définies dans les différentes classes de base. De même, nous pouvons avoir aussi des problèmes d'initialisation des différents champs des classes de base. C'est pour cela que vous trouverez l'ensemble des aspects relatifs à l'héritage multiple dans cette [[in204:cpp:syntax:class:deriving:multiple|section]]. | + | [[cpp:syntax:class:deriving:destructor|Définir les destructeurs dans une classe dérivée]] |