IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Contribuez C++ Discussion :

[FAQ] Différence entre Init() et ctor


Sujet :

Contribuez C++

  1. #1
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut [FAQ] Différence entre Init() et ctor
    Bonjour,

    je propose un ajout dans la faq "Quelles sont les différences fondamentales entre le constructeur d'une classe et sa méthode Init() ?"

    Il est écrit qu'il existe 2 solutions.
    J'en vois une troisième: le scope guard.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  2. #2
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Où donc veux-tu en venir?
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Il est écrit:
    Si init lève une exception, la mémoire (ou autres ressources) allouée pour t n'est jamais libérée
    Le scope guard est également une solution qui permet de régler ce problème (bien que ce ne soit pas fait pour ça à l'origine):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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 ScopeGuard
    {
    public:
      ScopeGuard () 
       : engaged_ (true) 
      {}
     
      ~ScopeGuard ()  
      { 
        if (engaged_) 
         { /* libération des ressources ici */} 
      }
     
      bool init( /* params */ )
      {
        // initialisation et acquisition ici, avec une gestion correcte des allocations (et libération en cas d'échec)
     
        // si tout s'est bien passé
        engaged_ = true; 
        return true;
      }
     
    private:
      bool engaged_;
    };
    C'est que parfois (souvent même j'ai l'impression) on ne peut pas acquérir tout dès le constructeur.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Si tu ne peux pas tout acquérir dès le constructeur, c'est que tu veux construire une voiture sans avoir fabriqué ou acheté le moteur.

    Ce que tu ne peux pas avoir automatiquement, demande le en argument, et tout devient simple.
    Si ce n'est pas encore le cas, raiie-le (*).
    Si c'est encore impossible, je pense que l'architecture est mal pensée.

    Je m'explique:

    Si j'ai besoin d'initialiser quelque chose que j'ai déjà construit, dans quel état était-il?

    Un objet est toujours dans un état connu et utilisable, il doit être construit ou ne pas être.


    * tiens, je l'aime bien, mon néologisme
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    C'est pas une troisième méthode, c'est une implémentation particulière de init().

    Qui peut rapidement poser des problèmes : si tu as plusieurs variables à initialiser, tu devras créer une variable bool pour chaque variable membre que tu veux initialiser, ce qui sera vite ingérable.

    La solution, c'est le RAII, c'est à dire initialiser dans le constructeur (et comme le dit leternel, si c'est pas possible, voir s'il n'y a pas un problème de conception).
    Et utiliser des pointeurs intelligents (voir pas de pointeur) pour les variables membres.

    Par contre, il faudra quand même mettre à jour cette faq (en fait, l'ensemble de la faq) pour le C++11 (auto_ptr...)

  6. #6
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    @leternel: J'ai du mal à comprendre ton message...
    Selon toi, il serait interdit de construire un objet partiellement puis de le remplir ensuite par aggregation? L'idiome scope guard n'a donc pas lieu d'être?
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  7. #7
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Pas interdit, le C++ est un langage permissif...
    Mais non respectueux du RAII, donc moins sur, moins robuste, moins lisible, etc

    L'approche C++ : le RAII
    Architecture Logicielle & Développement - RAII !

  8. #8
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Citation Envoyé par r0d
    C'est bon, je sais ce qu'est le RAII quand-même.
    J'ai parfois l'impression que nous ne vivons pas sur la même planète. C'est super tous ces idiomes et partons, je suis le premier à en défendre l'usage. Mais dans la vrai vie on ne peut pas toujours faire ce qu'on veut, en particulier lorsqu'on travaille sur de l'existant. Et c'est la raison pour laquelle ce type de solutions existent.
    Pas de problème, c'est bien le même monde : je passe beaucoup plus de temps à maintenir de l'existant que de partir d'une feuille (de code) vierge

    Après, effectivement, si on peut pas modifier un code existant pour mettre en place le RAII, que l'on peut pas utiliser des pointeurs intelligents (fournit pas le C++11, boost, Qt, ou même que l'on aurait créé), cela devient compliqué à faire du code robuste.

    Le code que tu présentes posera vite des problèmes à mon sens (peut être d'autres auront un avis différents). Pour les objets complexes, j'aurais tendance à préféré des DP composite, builder, etc. Ou un DP wrapper pour sécuriser/isoler un code non robuste (typiquement une lib C utilisée en C++), etc


    Pour revenir plus spécifiquement à la FAQ, il me semble que c'est pas une correction à faire (parce que c'est pas une troisième solution, mais une implémentation spécifique de init ; et qu'il faut bien faire des choix quand au contenu de la faq)

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,

    En fait, nous pourrions peut etre en effet parler du scope guard, tout en précisant clairement qu'il s'agit d'une manière de travailler qui devrait être réservée au cas "exceptionnel" dans lequel on n'a pas d'autre solution (comme la maintenance d'un code existant), mais qui est symptomatique d'un problème de conception à la base.

    Je suis le premier à prôner le fait qu'un objet se doit d'être directement dans un état cohérent dés la fin du constructeur, mais j'ai aussi rencontré suffisamment de code pour savoir qu'il est parfois difficile de faire en sorte que ce soit toujours le cas
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  10. #10
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Personnellement, au boulot, je refuse systématiquement d'écrire du code qui n'est pas "constructor coherent".
    Et chaque fois, c'est accepté, et je fais sauter un millier de lignes de code (oui, le projet est monstrueux…)

    D'ailleurs, en général, quand j'en parle tous les développeurs semblent d'accord pour reconnaître que le code est "mal écrit" dans ces portions.

    La seule "exception" est les "arguments nommés", que j'utilise pour la construction de requete sql, par exemple.
    Encore que, "construction en cours" peut-être vu comme un état valide d'un "RequestBuilder"
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  11. #11
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Il y a quelque chose qui m'échappe dans tout ça.
    Déjà, dans ladite faq, je lis:
    Dans certains cas, on n'a pas assez d'infos à la construction de l'objet pour tout initialiser [...]
    Selon vous, cette phrase est à bannir? Et donc toute la faq? Puisque si je comprend bien, vous êtes en train de dire que la fonction init() ne devrait pas exister? Répondez-moi s'il vous plait, je ne comprend pas votre position.

    Vous n'êtes jamais tombé sur des problèmes où vous n'avez pas ce qu'il faut pour construire un objet complètement au moment où l'on a pourtant besoin de lui?

    gbdivers par exemple, je lis que tu es bio-informaticien. Tu dois bien avoir rencontré des problèmes de data-mining où tu dois construire une structure à partir de set de données partiellement complets non? Et encore ça s'est rien, le vrai problème c'est lorsqu'on doit construire un modèle à partir de ces données incomplètes et qu'on ne sait pas à l'avance de quelle façon on va construire cet objet.

    Quant à "supprimer des milliers de ligne de code", parfois c'est juste pas possible. Par exemple (et sans parler du cas - pourtant fréquent - du problème de temps), lorsque ces milliers de lignes de code effectuent un traitement tellement complexe qu'il nous faudrait des années pour seulement comprendre ce que fait ce code.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  12. #12
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    A voir tes questions, je crois que l'on pas été assez précis et exacte sur les explications.

    un objet se doit d'être directement dans un état cohérent dés la fin du constructeur
    En fait, la construction est différent de l'initialisation. La fonction init est surement mal nommé, son but n'est pas d'initialiser la classe (c'est ce que fait assign pour vector par exemple), mais de la "construire" (selon la définition de koala01, en particulier que l'invariant de classe soit respecté à la fin du constructeur)
    Il n'est pas aberrant de créer un conteneur vide et de le remplir avec des push_back, on sait bien qu'il n'est pas possible dans la plupart des cas de créer le vecteur directement rempli.

    Ici on parle bien d'utiliser une fonction init en remplacement du constructeur, donc une fonction qui chargée de mettre la classe dans un étant cohérent. Ce qui implique que l'objet n'est pas dans un étant cohérent entre l'appel du constructeur et l'appel de init (si c'est pas le cas, alors c'est que le constructeur a fait son boulot et que la fonction init n'est pas en remplacement du constructeur, mais une vraie fonction d'initialisation)

    Il faut peut être reformuler certaines phrases...

  13. #13
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    auto_ptr<> est une capsule à la RAII. Aujourd'hui on dirait unique_ptr. C'est un scoped_guard. Un peu particulier certes, mais cela en est un. C'est ici un détail par rapport à comment écrire cette sorte de factory-method qui fait construction + post-init polymorphiqme.

    Sinon, bonne idée que de déterrer ce point de FAQ car il n'adresse pas clairement ce qui est vraiment important.

    init() est un truc hérité de l'enseignement syntaxique par delta du C++ (j'invente les mots ; mais ils viennent d'une rumination pour une présentation que j'ai faite dans la boite pour inciter de migrer du C++ historique au C++ moderne). Au départ le C dont on fait vite le tour de la syntaxe. Le Pascal omniprésent dans l'éducation (chez nous du moins). Et des profs dans les années fin 80 à 90+ qui veulent présenter le C++.
    Donc il partent du C. Voilà une structure, on en fait un premier objet. Souvenez-vous les structures il faut les initialiser. Pouf une fonction init(). Ils continuent à présenter divers trucs et puis à un moment ils reviennent sur l'initialisation des données (notez le soucis au passage : la POO part d'une agrégation de données, et non de services rendus) et disent: ouais mais en fait il y a un truc fait pour initialiser -> les constructeurs.
    [notez l'approche itérative, d'où le "delta", qui ne s'intéresse qu'à présenter la syntaxe]

    Sauf que c'est déjà trop tard, les élèves ont eu deux semaines de TP entre temps à avoir utilisé des fonctions init() qui semblent remplir le besoin. Ils ont déjà pris la mauvaise habitude et sans même savoir pourquoi c'est une mauvaise habitude, parce que l'on ne leur a jamais parlé de cet invariant dynamique/"structurel" (pas le bon mot)

    En effet, la fonction init() oblige à gérer l'invariant "objet utilisable" de façon dynamique : avec un membre isReady/isInitialized, et un test de cette variable en début de chaque fonction. Tandis que l'approche constructeur nous assure un invariant statique : "par construction" (souvenez-vous des démonstrations en maths, c'est le même "par construction"), nous sommes assurés que si l'objet existe alors il est utilisable => pas de test à faire!

    Ce qui me fait penser à cette analyse d'une interview de Stroustrup, où il est cité à dire: http://www.theregister.co.uk/2012/12...n_c_plus_plus/
    Citation Envoyé par Stroustrup
    I try to focus on things that lead to correct programs fairly easily. I emphasise the use of constructors to establish invariance, to acquire resources, destructors for cleaning up [...]
    L'invariance. C'est la clé du pourquoi les fonctions init c'est mal.

    Au final, je vois deux intérêts à init() :
    - la post-initialisation polymorphique, dont le point de FAQ parle
    - le code pré-exceptions -> je vous renvoie aux règles de codage chez google qui interdisent les exceptions (car base de code sans exceptions trop volumineuse pour changer ça) et qui demandent donc à utiliser des fonctions init().
    Mais init() reste le cousin pauvre du constructeur. À bannir par défaut.

    [Ce peut valoir le coup d'insister sur cette histoire d'invariance dans le point de FAQ]
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  14. #14
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    et un test de cette variable en début de chaque fonction
    Juste une remarque. Si ce test est réellement effectué pour toutes les fonctions, alors a priori, la classe est toujours cohérente (on a bien la validation systématique faite pas le concepteur de la classe et pas par l'utilisateur, simplement l'invariant de classe I devient "si isInitialized alors I, sinon rien" et il est toujours respecté, pas d'états et de comportements indéterminés)

    En pratique, c'est jamais fait comme ça (surcout, oubli), d'ou la nécessité de l'appel d'init par l'utilisateur (et la faq dans ce sens, avec la phrase "l'utilisateur doit l'appeler manuellement (risque d'oubli)" sur le risque d'erreur par l'utilisateur)

  15. #15
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    Juste une remarque. Si ce test est réellement effectué pour toutes les fonctions, alors a priori, la classe est toujours cohérente (on a bien la validation systématique faite pas le concepteur de la classe et pas par l'utilisateur, simplement l'invariant de classe I devient "si isInitialized alors I, sinon rien" et il est toujours respecté, pas d'états et de comportements indéterminés)

    En pratique, c'est jamais fait comme ça (surcout, oubli), d'ou la nécessité de l'appel d'init par l'utilisateur (et la faq dans ce sens, avec la phrase "l'utilisateur doit l'appeler manuellement (risque d'oubli)" sur le risque d'erreur par l'utilisateur)
    En fait, il s'agit plus d'une précondition pour tout usage d'un objet autre que l'appel à init et d'une postcondition pour le fait d'appeler init

    Mais la question se pose alors dans ces termes :
    Que va-t-il se passer si la précondition n'est pas respectée
    La réponse classique à cette question est généralement
    Ben, ca, mon vieux, tant pis pour toi... avec beaucoup de chance, ca passera, sinon...
    Et on l'en revient à un problème similaire à ce qui fait que l'on préfère les références aux pointeurs "chaque fois que possible" car cela sous entend que toute fonction commence par ... un gros test d'initialisation (le lien entre un if(ptr) et un if (object.initialized() ) est flagrant )

    Edit Cependant, on ne peut pas prétendre qu'un objet sur lequel init n'aurait pas été appelé soit "cohérent".

    La preuve en est que, si init n'a pas été appelée sur un objet, on ne peut absolument pas préjuger du résultat et qu'il faut donc s'assurer de la cohérence de l'objet au travers du test
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  16. #16
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    A voir tes questions, je crois que l'on pas été assez précis et exacte sur les explications.
    Il me semble, en effet. D'ailleurs, vous ne répondez pas à mes questions.

    Je vais essayer de trouver une formulation avec laquelle nous soyons tous d'accord: l'init c'est comme les pointeurs, à n'utiliser que lorsqu'on ne peut pas faire autrement (formulation qui sous-entend que, parfois, on ne peut peut pas faire autrement).

    Après, Luc a raison sur le fait que derrière cette discussion, il y a un problème plus théorique concernant l'état d'un objet. Je suis bien incapable de formuler cela clairement, mais en gros, je pense que ce qui est important c'est l'état de l'objet (à un instant donné), et pas la façon dont l'objet est construit et/ou modifié. Donc l'invariance, mais peut-être pas uniquement (d'ailleurs, en parlant d'invariance, il serait peut-être bien de parler des assertions dans la faq?).

    Il y a également un problème de définitions:
    - Quand est-ce qu'un objet est "utilisable"? En effet, on peut imaginer un objet construit partiellement, avec une sémantique telle que si certaines partie manquent cela ait du sens (un exemple: sachant que telle donnée est manquante, cela permet de se situer dans l'arbre d'un traitement sur lequel on a pas le contrôle. D'ailleurs, de façon plus globale, l'absence de donnée a en fait parfois du sens.).
    - Qu'est-ce que l'invariance en c++? Je n'ai trouvé aucune définition qui me convienne. Une classe (un type en général) n'est pas un invariant en soi, mais elle définit des invariants. Mais comment appréhender ces invariants lorsqu'on a une classe qui évolue (par exemple une machine à état).
    - Qu'entendez-vous par "constructor coherent"?

    Enfin, un point qui n'est toujours pas clair pour moi: quid des constructeurs pas défaut? On est très souvent obligé de passer par un constructeur par défaut (stockage dans un conteneur template, basiquement). Est-ce que, dans ce cas, une fonction init() n'est pas envisageable? Un exemple de code pour illustrer ma question:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    main()
    {
      std::vector<MyObject> v(5);
      // arrivé là, nos 5 objets sont déjà construits via le default ctor. 
      // Ils ne sont pas forcément utilisables directement pour autant
    }
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  17. #17
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    main()
    {
      std::vector<MyObject> v(5);
      // arrivé là, nos 5 objets sont déjà construits via le default ctor. 
      // Ils ne sont pas forcément utilisables directement pour autant
    }
    S'ils ne sont pas utilisables, c'est parce que le constructeur par défaut est pourri, car il ne fait pas ce qu'il faut.

    Si tu fais un std::vector<char> v(5); ton vector contient cinq char, pleinement utilisables.

    un objet est utilisable si toute fonction membre est appelable et n'échoue pas hors interface.

    Par exemple, si tu as un std::fstream, il est forcément utilisable. Sinon, le constructeur aurait throwé, et tu n'aurais pas ton fstream.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  18. #18
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    - Invariant : N'importe quel propriété P que doit vérifier un objet de type T.

    - propriété : N'importe quel assertion (vérifiable ? (*)) sur un objet.

    - ctor coherent : Dans le contexte, un constructeur qui, si aucune exception n'est lancée, va créer un objet respectant les invariants associés au type (sinon rien n'est crée).

    Au final l'intérêt pratique des fonctions init est assez faible je pense :
    - Pour le code interdit d'exception
    - Pour le besoin de ctor polymorphe comme l'a expliqué Luc, cependant je me demande si il existe autant de cas que ça où ce besoin est bien fondé et pas lié à une conception bancale.
    - En fonction à visibilité restreinte pour factoriser le code, inutile en C++11 (delegating ctor).

    Pour les exemples de BDD, data mining, etc, c'est quand même aux développeur de fixer les invariants de sa classe (tout en restant cohérant), il a aussi le pouvoir d'introduire des intermédiaires (aux invariants plus faible), au final je suis convaincu que l'on peut aboutir à des designs sur ces éléments sans avoir besoin d'init.

    (*) De manire "théorique" le vérifiable ne sert à rien, de manière pratique, le résultat d'une assertion non vérifiable ne doit pas avoir réellement d'impact.

  19. #19
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    - Invariant : N'importe quel propriété P que doit vérifier un objet de type T.

    - propriété : N'importe quel assertion (vérifiable ? (*)) sur un objet.
    Je craint que cela ne suffise pas, du moins pas dans le domaine du développement logiciel. Prenons l'exemple d'un objet qui a deux états et une fonction doSomething(). Cette fonction fera des choses différentes selon l'état de l'objet; par conséquent, notre objet aura des propriétés différentes selon son état. Pourtant, au niveau du paradigme objet, le type MonObjet::doSomething() sera invariant (une fonction peut être considéré comme un type).
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  20. #20
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par leternel Voir le message
    un objet est utilisable si toute fonction membre est appelable et n'échoue pas hors interface.
    Si ladite fonction lance une exception, tu considères cela comme un échec?
    Si oui, étant donné que toute fonction peut potentiellement lever une exception, alors aucun objet n'est utilisable.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

Discussions similaires

  1. Différence entre deux extractions : données init
    Par Christophe P. dans le forum Développement de jobs
    Réponses: 2
    Dernier message: 03/03/2015, 09h50
  2. Différence entre un "bidouilleur" et un Pro ?
    Par christ_mallet dans le forum Débats sur le développement - Le Best Of
    Réponses: 290
    Dernier message: 28/11/2011, 10h53
  3. [ZF 1.11] Différence entre indexAction() et init()
    Par Anto__ dans le forum MVC
    Réponses: 2
    Dernier message: 20/06/2011, 23h22
  4. [PR-2000] [FAQ]Différences entre type de taches
    Par jokergabber dans le forum Project
    Réponses: 1
    Dernier message: 03/02/2010, 07h38
  5. Réponses: 3
    Dernier message: 07/05/2002, 16h06

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo