Salut tous le monde quelqu'un pourrait t'il m'expliquait l'utilité des constructeurs et destructeurs
merci pour vos reponses
Version imprimable
Salut tous le monde quelqu'un pourrait t'il m'expliquait l'utilité des constructeurs et destructeurs
merci pour vos reponses
C'est vraiment la base de la programmation. Il y a nombre d'explications sur internet ou dans des bouquins, pas de raison de faire une explication spéciale, commence par regarder sur la page Cours C++. Et si tu veux une explication courte et simple, direction la FAQ -> http://c.developpez.com/faq/cpp/?page=constructeur et http://c.developpez.com/faq/cpp/?page=destructeur
Y aussi ca que je comprend pas: qu'est ce qu'on appelle les objets en c++ ?
T'inquiète dans 2 minutes quelqu'un va débarquer chez toi pour te donner un petit cours privé . . .
As-tu au moins pris le temps de lire et de te renseigner (plus que de lire une fois) à ces sujets? As-tu essayé de mettre en oeuvre ces concepts dans des petits programmes?
Le concept d'objet n'est pas limité au c++!
J'ai l'impression qu'il te manque vraiment les concepts de base de la POO, qui sont certainement les plus difficile à définir à la va-vite, dans le cadre d'un post comme on peut en faire ici. Surtout qu'on ne sait pas sur quels concepts connus de toi on peut s'appuyer.
En C++, quand tu définis :
tu fais plusieurs choses. Tu déclare un objet en mémoire. Son type est entier, et il sera accessible par la variable i. Du plus, tu l'initialises avec la valeur 5.Code:int i = 5;
Certains restreignent le mot objet au cas où le type de la variable n'est pas un type de base du C++, mais un type crée par l'utilisateur, mais à la base, un objet n'est rien d'autre que ça. Et le constructeur est ce qui permet lors de la création d'initialiser correctement cet objet (comme dans l'exemple le fait qu'on puisse initialiser l'entier à 5).
Le mot objet en C et en C++ a un sens particulier qui n'est pas nécessairement celui de la POO.
Si par exmple j'ai ca qu'est ce que je peux inistialiser:
Code:
1
2
3
4
5
6
7 class nombre { public : nombre() int nb1; int nb2; };
Salut elmodeno,
Encore une fois je te conseil de t'interressé a des tutoriaux sur la POO avant de te lancer dans un projet!
http://www.developpez.com/c/megacours/book1.html
En ce qui concerne ce code du declare un objet apeler nombre et tu as un constructeur -> nombre()Citation:
Code:
1
2
3
4
5
6
7 class nombre { public : nombre() int nb1; int nb2; };
ensuite ton objet fait reference a deux entiers nb1 et nb2 que tu peux initialisé dans ton constructeur....
bon courage .....
Mais je peux tres bien initialisé nb1 et nb2 sans constructeur en faisant simplement ca :Citation:
Envoyé par Jérémy Lefevre
Code:
1
2 nombre.nb1=10; nombre.nb2=20;
je vais prendre un exemple d'une liste chainée:
à la fin du programme, pour libérer la mémoire sans destructeur, tu dois parcourir toutes la liste chaine pour detruire suivant en commencant par le dernier.Code:
1
2
3
4
5
6
7
8
9
10
11
12 class noeud { public: int valeur; noeud *suivant; noeud () { suivant = NULL; }; ~noeud () { if (suivant != NULL) delete suivant; }; }; noeud *tete;
avec le destructeur, tu efface le premier et le destructeur s'occupe d'effacer le reste
C'est un problème d'encapsulation des données. Lors de la conception de ta nombre, tu représentes les données dont tu as besoin à l'aide de deux membres de type int. Il s'agit la d'un choix d'implantation que tu as fais lors de la conception de ta classe nombre.Citation:
Envoyé par elmodeno
Maintenant, il se peut très bien que tu décides un jour de remplacer pour une raison x ou y les variables nb1 et nb2 par un tableau:
Si le code client qui utilise la classe nombre et s'appuye sur une initialisation explicite par accès direct à nb1 et nb2:Code:
1
2
3
4
5 class nombre { public: int nb[2]; }
il y aura un problème au moment ou tu décides de changer certains détails d'implémentation (par exemple, utilisation d'un tableau au lieu de 2 entiers).Code:
1
2
3
4
5
6
7
8 int main() { nombre n; n.nb1 = 5; n.nb2 = 10; retour 0; }
Dans ce contexte, l'utilisation d'un constructeur permet d'assurer une initialisation propre de l'objet de type nombre sans créer de dépendance entre le code client et la structure interne de la classe. Exemple:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 /* Exemple 1: implantation de nombre avec 2 entiers */ class nombre { private: int nb1; int nb2; public: nombre(int n1, int n2) { nb1 = n1; nb2 = n2; } int getNombre1(void) const { return nb1; } int getNombre2(void) const { return nb2; } }
Ton code client ne pourra maintenant plus accéder aux membres nb1, nb2 ou au tableau nb (qui sont déclarés private). L'initialisation doit obligatoirement passer par le constructeur, et les méthodes getNombre1 et getNombre2 permettent au client d'obenir les deux valeurs représentées par la classe nombre. Le même code client peut être utilisé quelque soit l'implantation de la classe client:Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 /* Exemple 2: implantation de nombre avec un tableau de 2 entiers */ class nombre { private: int nb[2]; public: nombre(int n1, int n2) { nb[0] = n1; nb[1] = n2; } int getNombre1(void) const { return nb[0]; } int getNombre2(void) const { return nb[1]; } }
Le constructeur, ainsi que les méthodes getNombre1 et getNombre2 représente ce qu'on appelle l'interface de ta classe. Si le code client communique avec un objet de type nombre uniquement via cette interface (on peut le forcer à cela en déclarer private les membres donnée de la classe), il est possible au concepteur de la classe nombre d'en modifier l'implantation sans affecter le fonctionnement du code client. Le bénéfice en matière de maintenance et de réutilisation de code n'est pas directement visible sur un code aussi simple, mais le gain est énorme sur un projet grandeur nature.Code:
1
2
3
4
5
6
7
8
9
10 #include <iostream> #include "nombre.h" int main() { nombre n(5,10); std::cout << n.getNombre1() << ", " << n.getNombre2() << std::endl; return 0; }
Thierry
Tu veux sûrement dire
Ce qui de toutes manières est mauvais à mon avis.Code:
1
2
3
4
5
6
7
8
9 class noeud { public: int valeur; noeud *suivant; noeud () : suivant(NULL) { } ~noeud () { delete suivant; } };
Quand tu détruis un noeud tu n'as pas à détruire l'ensemble de la liste.
Salut,
Tu devrais vraiment commencer par t'intéresser aux différents concepts qui régissent la programmation orientée objet...
En effet, outre les constructeurs et les destructeurs, il y a un concept qui te fait vraiment défaut, c'est celui de la visibilité ou de "l'encapsulation" des données...
L'idée, c'est que tout le monde ne doit pas pouvoir chipoter à tout ce qui fait ton objet...
Ainsi, dans la vie de tous les jours...
Quelque soit la chose que tu puisse prendre en main, tu ne pourras pas faire n'importe quoi...
Tu pourras faire certaines actions dessus, tu pourras apréhender certaines caractéristiques, tu pourras, éventuellement, au moyens de certaines actions influencer sur certaines caractéristiques, tu pourras te rendre compte que certaines autres choses ont des caractéristiques identiques, mais tu ne verra pas *forcément* tout ce qui construit cette chose...
Si on prend une tasse, par exemple:
- Tu peux effectuer des action simples:
- la remplir
- la vider/boire son contenu
- la laver
- ...
- Tu peux apréhender certaines de ses caractéristiques sans pouvoir les changer
- sa forme
- sa couleur
- sa matière
- sa capacité
- ...
Certaines de ces caractéristiques sont d'ailleurs partagées avec d'autres choses (avec un verre, ou une cannette de soda, par exemple)- Enfin, il y a certaines choses que, bien qu'elles fassent partie intégrante de la tasse, tu ne peux pas voir de l'extérieur:
- la composition réelle de la matière
- le coéfiscient de dissipation de la chaleur
- la conductivité électrique
- la dureté du matériaux
- tout ce qui fait que la tasse est tasse et non canette de soda
- ...
Hé bien, en programmation orientée objet, l'idée est exactement la meme: tu vas fournir "quelque chose" à l'utilisateur, mais tu vas veiller à ce qu'il ne fasse pas n'importe quoi n'importe comment avec ce que tu lui fournis...
Tu lui présentera un objet sur lequel il pourra agir au moyen d'actions déclarées en publique (qui modifieront éventuellement des éléments qui sont cachés de l'utilisateur), qui peut partager certaines informations ou actions déclarées en protégé avec d'autres choses du meme genre, mais qui garderont surement quelques "secrets", déclarés en privé, nécessaires pour "donner sa forme" à l'objet, mais que l'utilisateur n'a besoin ni de voir/connaitre, ni de pouvoir les modifier...
Dans l'exemple que tu donnes:
il y a de fortes chances pour que tu ne veuilles pas que l'on puisse changer les valeurs de nb1 et nb2 n'importe comment...Code:
1
2
3
4
5
6
7 class nombre { public : nombre() int nb1; int nb2; };
Tu vas donc décider de les mettre en protected si tu prévois qu'une classe dérivée doit pouvoir les modifier, en private si tu prévois que seule la classe nombre doit pouvoir y accéder/les modifier...
Seulement, voilà... La question qui reste à se poser, c'est "A quoi va servir ta classe :question:"
Je vais, pour te faire comprendre la modifier un peu:
Ta classe qui contient trois réels ne se contente plus d'etre simplement une structure de trois réels...Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 class coord3D { public: coord3D():X(5.0f),Y(10.0f),Z(15.0f){} coord3D(float x, float y, float z):X(x),Y(y),Z(z){} ~coord3D(){} const float* GetAsTab() const { int tab[3]={X,Y,Z}; return tab; } const float GetX() const {return X;} const float GetY() const {return Y;} const float GetZ() const {return Z;} const float GetNormal() const { float retour=(x*x/2)+(y*y/2)+(z*z/2); return retour; } private: float X; float Y; float Z; };
C'est devenu une coordonnée trois dimentions, dont l'utilisateur n'a meme pas besoin de savoir comment elle est construite...
Tout ce qu'il sait, c'est qu'il peut récupérer au choix un tableau des trois dimentions, la valeur de chaque dimention prise séparément, ainsi que la valeur de la normale pour la position indiquée...
Il n'a pas besoin de savoir comment ca fonctionne à l'intérieur, et il n'a aucune raison, une fois qu'il a disposé son point dans un univers à trois dimentions, d'aller le modifier...
Et c'est là que tout l'intéret des constructeurs/destructeurs/foncteurs/accesseurs entre en jeu...
Justement c'est ce que l'initiateur de ce sujet cherche , une explication sur les constructeurs et destructeurs...pas qu'on lui tire dessus...Citation:
Envoyé par koala01
n'oublie pas que toi aussi tu as été débutant
Mais je ne lui ai pas tiré dessus (ou du moins, je n'avais pas l'intention de le faire)...Citation:
Envoyé par Mat.M
La preuve, j'ai essayé de lui présenter les choses sous un angle "compréhensible"...
En effet, constructeurs et destructeurs (ainsi que tous les *teurs, d'ailleurs) ne prennent un sens qu'à partir du moment où l'on a compris l'intéret de ne pas mettre tout en visibilité "public" ;)
L'astuce, c'est que pour comprendre l'intéret de ne pas tout mettre en public, il faut avoir une idée de ce que permettent les autres solutions, et de pourquoi préférer mettre quelque chose en protected ou en private...
Là, je ne suis pas d'accord.Citation:
Envoyé par koala01
La RAII n'a pas grand-chose à voir avec la visibilité, par exemple...
Ok y'a pas de mal :DCitation:
Envoyé par koala01
Merci de vos reponses mais ce que je comprend pas c'est que quel rapport y a entre l'encapsulation et les constructeurs et svp essayez de m'expliquer les constructeurs le plus facilement possible sans utiliser de termes tres difficile
Soit la classe suivante qui représente un Point:
Dans ton code client (par exemple la fonction main), tu décides de créer (on dit généralement instancier) et initialiser un objet de type Point. Tu procèdes donc de la manière suivante:Code:
1
2
3
4
5 class Point { public: int x; int y; }
Mettons que je décide d'écrire la classe Point différemment:Code:
1
2
3
4
5
6
7
8 int main() { Point p; p.x = 5; p.y = 10; return 0; }
J'ai maintenant décidé de représenter l'abscisse et l'ordonnée de ton point à l'aide d'un tableau. Seulement voilà, ton code client, situé dans le corps de la fonction main, ne fonctionne plus, à cause du fait que tu as appelé explicitement les membres x et y pour initier ton objet.Code:
1
2
3 class Point { int xy[2]; }
A quoi sert le constructeur et qu'est-ce que l'encapsulation des données?
Comme tu l'as vu dans le code ci-dessus, une modification de la structure interne de ma classe Point a entraîné un disfonctionnement de ton code client.
En plus de forcer l'initialisation d'un objet lors de sa création, l'utilisation d'un constructeur permet de cacher au client les détails d'implantation. En effet, en tant qu'utilisateur de la classe Point, tu n'as que faire de savoir que l'abscisse est représenté avec un membre appelé x, et que l'ordonnée est représentée par un membre y, ou que x et y sont représentés ensemble dans un tableau. Ce que tu veux représenter, c'est un point de coordonnées (5, 10), et c'est tout!.
Le constructeur va te permettre ici de cacher à l'utilisateur de la classe les détails d'implantation. Je peux donc écrire ma classe Point de deux manière:
De cette manière, quelle que soit la variante utilisée pour implanter ma classe Point, toi, en tant qu'utilisateur désireux de créer un objet de type Point, tu n'as pas besoin de savoir si j'ai utiliser deux entiers ou un tableau pour représenter ton point de coordonnées (5, 10).Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 /*Variante 1: les coordonnées sont représentées par deux variables x et y*/ class Point { int x; int y; public: Point (int abs, int ord) { x=abs; y=ord; } }; /*Variante 2: les coordonnées sont représentées dans un tableau xy*/ class Point { int xy[2]; public: Point (int abs, int ord) { xy[0]=abs; xy[1]=ord; } };
Le constructeur m'assure, moi, le développeur de la classe Point, que ton code client fonctionnera toujours, quelque soit l'implantation que j'ai choisi pour représenter les données de la classe Point. Toi, en tant qu'utilisateur, tu n'as pas besoin de connaître les détails d'implantation de ma classe Point. Il te suffit de connaître l'interface proposée par le constructeur. Tu veux représenter un point de coordonnée (5, 10) et rien de plus, et le prototype du constructeur te renseigne sur la manière d'y parvenir.Code:
1
2
3
4
5 int main() { Point p(5, 10); // réalise la création de l'objet et l'initialisation return 0; }
L'utilisation du constructeur t'assure que l'initialisation de l'objet est effectué dès sa création, et ceci sans que tu doive te documenter sur la structure des données internes de ton objet.
A quoi sert le destructeur?
Moi, le développeur de la classe Point, après une immense recherche bibliographique, j'ai découvert qu'il était préférable d'utiliser l'allocation dynamique lors de la représentation interne de mon point. J'écris donc une nouvelle implantation de ma fameuse et désormais célèbre classe Point
Comme des millions d'utilisateurs utilisent désormais ma classe, je veux que les utilisateurs comme toi qui utilise Point comme dans le code ci-dessous puissent bénéficier des importantes améliorations et optimisations que j'ai apporté au code, sans modifier une seule ligne du code client qu'ils ont écrit. Ainsi, ton code:Code:
1
2
3
4
5
6
7
8
9
10
11
12
13 class Point { int *xy; public: // Constructeur de Point: on alloue maintenant dynamiquement la mémoire pour le tableau xy Point(int abs, int ord) { xy = new int[2]; xy[0] = abs; xy[1] = ord; } // Destructeur: on a alloué de l'espace dynamiquement, il faut donc le libérer ~Point() { delete[] xy; } }
doit fonctionner indépendamment des changement que j'ai apporté au code de Point. Pour toi, rien n'a changé et c'est bien le dernier de tes soucis de savoir que ton objet a alloué dynamiquementde la mémoire. Toutefois, il faut bien que la mémoire allouée soit libérée lors de la destruction de l'objet. Ce n'est pas toi qui va faire appel à l'opérateur delete[], car tu ne connais rien de l'implantation de ma classe Point. Il faut donc que l'espace alloué soit libéré automatiquement à la destruction de l'objet. C'est à cela que sert le destructeur de la classe, à libérer des resources proprement et automatiquement à la destruction d'un objet (je simplifie un peu, là).Code:
1
2
3
4
5 int main() { Point p(5, 10); return 0; }
Voilà, j'espère avoir contribué un peu à clarifier certaines choses. Meilleures salutations
Thierry