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

C++ Discussion :

Classe avec constructeurs multiples


Sujet :

C++

  1. #1
    Membre émérite

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Points : 2 328
    Points
    2 328
    Par défaut Classe avec constructeurs multiples
    Bonjour à tous.

    J'essaie de faire une classe qui contient 2 constructeurs.
    Et je veux définir le second constructeur en faisant appel au premier comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class A {
    public:
    	A(std::vector<int> vec):_vec(vec){};
    	A(int x, int y){
    			std::vector<int> vec(2);
    			vec[0]=x; vec[1]=y;
    			A(vec);
    	};
    protected:
    	std::vector<int> _vec;
    };
    sauf que la ligne "A(vec)" produit l'erreur suivante :
    test.cxx:48:6: error: redefinition of 'vec' with a different type: 'A' vs 'std::vector<int>'
    A(vec);

    test.cxx:46:21: note: previous definition is here
    std::vector<int> vec(2);

    1 error generated.
    Quelqu'un peut-il m'éclairé ?

    Merci

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 629
    Points : 10 554
    Points
    10 554
    Par défaut
    Déjà ton code est bizarre
    Pourquoi vouloir créer un temporaire pour initialiser son attribut en se cassant le trognon à appeler un autre constructeur?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    A(int x, int y): _vec(2, 0) {
        _vec[0] = x;
        _vec[1] = y;
    };
    Et ensuite il me semble que tu ne peux pas le faire, et il faut créer une méthode qui initialise ton vecteur et qui sera appelée dans les constructeurs

    Édit: Que c'est moche les tirets-bas dans les noms des attributs, et d'ailleurs il me semble que c'est déconseillé
    Moi, je suis extrémiste: je ne précède même pas le nom de mes attributs par "m_" (ni de n'importe quelle autre indication)

  3. #3
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Depuis C++11 il est possible qu'un constructeur appel un autre constructeur (possible uniquement dans la liste d'initialisation)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct S {
      S() :S(42) {}
     
      S(int i) : i(i) {}
     
      int i;
    };
     
    S().i == 42
    Mais il y a encore plus simple: fournir au vecteur les valeurs de départ.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    A(int x, int y): _vec({x,y}) {
    };

  4. #4
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par foetus Voir le message
    Déjà ton code est bizarre
    Pourquoi vouloir créer un temporaire pour initialiser son attribut en se cassant le trognon à appeler un autre constructeur?
    Même si ici ce n'est pas très bien appliqué, c'est très commode comme technique .

  5. #5
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par foetus Voir le message
    Oui c'est la technique C++11 pour manipuler les temporaires
    Mais a 2-3 subtilités cela revient à ce que j'ai dit : à créer une autre méthode (donc ici swap) et l'appeler des constructeurs et des opérateurs
    Pas du tout, cela existait bien avant C++11.

    Même si « Code < C++11 à l'arrache non testé », ton code est "archi faux".

    Citation Envoyé par foetus Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    protected:
     
        std::vector<int> vec;
    };
    Par convention (pas du tout obligatoire), j'utiliserais m_vector au lieu de vec.
    Pourquoi mettre cette donnée membre en protected ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    private:
        std::vector<int> m_vector;
    Et je mettrais le constructeur par défaut, qui a du sens (à partir de là tout est public) :
    C++11
    C++03
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A() : m_vector() { } // A() { } devrait suffire
    Citation Envoyé par foetus Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    A(std::vector<int> init_vec) {
            swap(init_vec);
        };
    La liste d'initialisation suffit amplement.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A(std::vector<int> const & vector) : m_vector(vector) { }
    Citation Envoyé par foetus Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    A(A& init_a) {
            std::vector<int> copy_vec(init_a.vec);
     
            swap(copy_vec);
        };
    Le constructeur de copie est généré automatiquement par le compilateur et fait la bonne chose, pas besoin de l'écrire ; mais si on devait le faire, ça serait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A(A const & a) : m_vector(a.m_vector) { }
    Là aussi, la liste d'initialisation suffit amplement, en plus si tu comptes utiliser l'idiome copy & swap, utiliser swap dans le constructeur par copie, ferait une boucle infinie (opérateur d'affectation qui appelle le constructeur par copie, qui appelle swap, qui appelle le constructeur par copie, ...)

    Citation Envoyé par foetus Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    A(int x, int y){
            std::vector<int> new_vec(2);
            new_vec[0] = x;
            new_vec[1] = y;
     
            swap(new_vec)
        };
    C++11
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A(int const x, int const y) : m_vector({ x, y }) { }
    C++03 (on pourrait faire une fonction qui prend deux entiers et retourne un vecteur avec ces deux entiers pour faire "comme en C++11")
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A(int const x, int const y) : m_vector(2) { m_vector[0] = x; m_vector[1] = y; }
    Citation Envoyé par foetus Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    // Parameter: neither pointer nor reference
        A& operator=(A other_a) {
            swap(other_a.vec);
     
            return *this;
        };
    L'opérateur d'affectation est généré automatiquement par le compilateur et fait la bonne chose, pas besoin de l'écrire ; mais si on devait le faire, ça serait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A & operator =(A const & a) { m_vector = a.vector; return *this; }
    Si on a une classe un peu plus compliquée, on peut utiliser l'idiome copy & swap
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A & operator =(A const & a) { A tmp(a); std::swap(*this, tmp); return *this; }
    La fonction template std::swap de la bibliothèque standard fait la bonne chose, si ce n'est pas le cas, on la spécialise. Voici une proposition :
    On écrit la fonction membre.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void swap(A & a)
    {
        using std::swap; // Pas nécessaire ici, mais c'est une bonne pratique (merci Flob90)
        swap(m_vector, a.m_vector);
    }
    Et voila la spécialisation (c'est une fonction libre).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    namespace std
    {
        template <>
        void swap(A & a, A & b) { a.swap(b); }
    }
    Et pour :
    Citation Envoyé par foetus Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // Parameter: reference
        void swap(std::vector<int>& other_vec) {
            vec.swap(other_vec);
        };
    std::vector<int> n'a pas de fonction membre swap(). Voir ma proposition au dessus.

    En fait si -_- (merci foetus).

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 965
    Points
    32 965
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par Ehonn Voir le message
    C++03
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A() : m_vector() { } // A() { } devrait suffire
    Ton constructeur est même totalement inutile : le constructeur par défaut des membres est déjà appelé implicitement.

    Citation Envoyé par Ehonn Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A & operator =(A const & a) { m_vector(a.vector); return *this; }
    T'as été un peu vite là, le std::vector n'a pas d'opérateur() qui prend en paramètres un autre vecteur.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A & operator =(A const & a) { m_vector = a.vector; return *this; }
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  7. #7
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Ton constructeur est même totalement inutile : le constructeur par défaut des membres est déjà appelé implicitement.
    Il est pas utilisé ici pour l'exemple mais c'est souvent pratique d'avoir un constructeur par défaut.
    Oui, le constructeur par défaut des données membre est appelé automatiquement mais pas celui des types de bases (ou alors, il ne fait pas la bonne chose). Autrement dit, int i; est différent de int i = int();. C'est pour cela que je préfère écrire explicitement l'initialisation par défaut des données membre (mais cela ne dépend que de moi).

    Citation Envoyé par Bousk Voir le message
    T'as été un peu vite là, le std::vector n'a pas d'opérateur() qui prend en paramètres un autre vecteur.
    Oups, désolé, copier-coller :s Je corrige, merci

  8. #8
    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
    Ce que vous dites est vrai, mais vous n'avez pas expliqué pourquoi c'est impossible d'appeler un autre constructeur comme lg_53 le faisait.

    La création d'une variable ou d'un pointeur se fait en deux étapes: allocation d'une zone de mémoire (sur la pile, ou sur le tas avec new), puis construction de la variable.

    Pour un type primitif, une enum ou une union la construction est simple: rien du tout, c'est à dire valeur arbitraire. (Undefined behaviour pour être précis)
    Pour un tableau, la règle est un peu plus subtile, selon ce qui est fourni (ou non) comme initialiseur et comme taille.
    Pour un type construit (struct, class), c'est le constructeur qui est appelé (constructeur par défaut s'il n'y a pas d'argument d'initialisation)

    Ce constructeur travaille sémantiquement: en deux temps, initialisation des classes parentes et variables membres, puis traitement global.
    L'initialisation est faite dans la liste d'initialisation (d'où le nom), et le traitement dans le corps du constructeur.

    On ne peut pas appeler un autre constructeur dans ce corps, parce que justement les variables ont déjà été initialisées (tout particulièrement les constantes et les références).

    C++11 ajoute la possibilité d'appeler un autre constructeur dans certains cas mais, pour la même raison, dans la liste d'initialisation.


    PS: il me semble que pour un constructeur, les types primitifs sont mis à 0.
    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

  9. #9
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    La réponse est simple: La syntaxe la plus intuitive pour appeler un constructeur hors de la liste d'initialisation est déjà utilisée pour instancier un objet temporaire.

    Exemple:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    A::A(int a)
    {
    	m_a = a;
    	m_str = "def";
    }
     
    A::A(std::string str)
    {
    	A(42); //Cette ligne ne fait pas ce que tu crois!
    	m_str = str;
    }
    La ligne 42 n'appelle pas un autre constructeur, mais crée un nouvel objet temporaire, immédiatement détruit!



    Mais ce n'est pas le seul problème: Imaginons que tu réussisses à faire ce que tu veux avec un placement new
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    A::A(std::string str)
    {
    	new(this) A(42);
    	m_str = str;
    }
    Félicitations, tu viens d'écraser des données déjà initialisées avec d'autres. Aucun destructeur ni opérateur = n'a été appelé, vu que le constructeur compte sur le fait que les données ne soient pas encore initialisées: Félicitations, tu as fait l'équivalent d'un memcpy() en C++. Très mauvais.
    C'est pour ça qu'on ne peut appeler un constructeur du même objet que dans la liste d'initialisation. Ou par copy-and-swap.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  10. #10
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Ehon, ce n'est pas fair play de chipoter sur les conventions de nommages (m_vector <-> vec).

    Citation Envoyé par leternel Voir le message
    PS: il me semble que pour un constructeur, les types primitifs sont mis à 0.
    Que veux-tu dire par là ? Les types primitifs ne sont mis à leur valeur par défaut (zéro la plupart du temps) qui si explicitement ajoutés dans un constructeur. Exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <iostream>
     
    struct Roger {
      int a;
      int b[10];
    };
     
    int main() {
      Roger r;
      std::cout << r.a << "\n";
      for(int i = 0; i < 10; ++i) std::cout << r.b[i];
      std::cout << std::endl;
      return 0;
    }
    Affiche une horreur du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    1964710984
    326184197008000419652801178070192327670
    Mais

    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
    #include <iostream>
     
    struct Roger {
      int a;
      int b[10];
      Roger()  : a(), b() {}
    };
     
    int main() {
      Roger r;
      std::cout << r.a << "\n";
      for(int i = 0; i < 10; ++i) std::cout << r.b[i];
      std::cout << std::endl;
      return 0;
    }
    Affiche bien:

    A noter, la syntaxe avec des parenthèses y compris pour l'array, qui est entièrement initialisé à zéro. En C++11, on peut utiliser des accolades à la place des parenthèses.
    Find me on github

  11. #11
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par jblecanard Voir le message
    Les types primitifs ne sont mis à leur valeur par défaut (zéro la plupart du temps) qui si explicitement ajoutés dans un constructeur.
    Selon le draft de la norme C++14 :
    int i; est une default-initialization qui, pour les types de base, n'initialise pas (§ 8.5.7)
    int() est une value-initialization qui, pour les types de base, est traitée par une zero-initialization (§ 8.5.8)

    Bref, la norme garantit que int() est 0.

  12. #12
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Ehonn Voir le message
    Selon le draft de la norme C++14 :
    int i; est une default-initialization qui, pour les types de base, n'initialise pas (§ 8.5.7)
    int() est une value-initialization qui, pour les types de base, est traitée par une zero-initialization (§ 8.5.8)

    Bref, la norme garantit que int() est 0.
    Oui, et c'est bien ce qu'explique jblecanard, si rien d'explicite n'est fait (i.e. constructeur généré par le compilateur, ou absence de l'entier dans la liste d'initialisation), l'entier à une valeur quelconque (default initialization), sinon non. Et que montre son exemple.

  13. #13
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par gl Voir le message
    Oui, et c'est bien ce qu'explique jblecanard.
    Et c'est ce que j'ai expliqué mais sans l'illustrer dans le message #8.

    Citation Envoyé par jblecanard Voir le message
    Les types primitifs ne sont mis à leur valeur par défaut (zéro la plupart du temps) qui si explicitement ajoutés dans un constructeur.
    Mon message était là juste pour dire que ce n'est pas « zéro la plupart du temps » mais bien « garantit à 0 lors d'une value-initialization » par la norme elle-même.

  14. #14
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    En effet, merci pour la précision, j'ai justement dit "la plupart du temps" parce que j'avais la flemme d'aller fouiller dans la norme pour voir si c'était bien zéro dans tous les cas .
    Find me on github

  15. #15
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Et moi je me suis dit « Attention ! si jblecanard dit « la plupart du temps » , je ferais mieux de vérifier ce que je pensais savoir » ^^

  16. #16
    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
    @Ehonn: La manière idiomatique d'utiliser swap c'est de faire un using std::swap; et d'utiliser un appel non qualifié. Ce n'est pas nécessaire dans l'exemple que tu donnes car ta classe n'est pas template, mais sinon ça le serait (donc autant le faire tout le temps).

    PS: Faute de frappe dans ta spécialisation de swap, c'est A pas T à priori.

  17. #17
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    C'est corrigé, merci

  18. #18
    Membre émérite

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Points : 2 328
    Points
    2 328
    Par défaut
    Fiou, vous m'en avez donné de la lecture !

    Merci pour vos réactions.
    Alors pour répondre au mieux à un peu tous le monde:
    1) Mon ECM est peut-être en effet trop minimaliste, puisque dans ce cas je peux effectivement procéder par liste d'initialisation du style:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A(int const x, int const y) : _vec(2) { _vec[0] = x; _vec[1] = y; }
    ou
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A(int x, int y): _vec({x,y}) {};
    Dans mon cas le problème est plus complexe, et je ne peux pas écrire mon second constructeur en faisant seulement une liste d'initialisation...
    Je vais peut-être travaillé sur un exemple un peu plus complet, qui s'approcherais un peu plus de ce que j'ai...

    2) Vous avez réalisé une petite digression sur la différence entre une default-initialization et une value-initialization. Je me serais cultivé une fois de plus, mais là n'est pas le coeur du problème je pense.

    3) Pour ce qui est du swap&copy, vous n'avez pas l'air tous d'accord sur la bonne manière de procéder, mais de toute façon si je travaille avec une structure plus complexe qu'un std::vector, ça semble laborieux à mettre en oeuvre.

    4) Pourquoi mettre en protected plutôt qu'en private? C'est un choix personnel. Tout simplement parce que si qqn n'a pas les droits pour modifier la classe mais qu'il souhaite l'améliorer,l'étoffer pour ses propres besoins, et bien il peut faire une classe fllle et a ainsi accès aux attributs librement en lecture et écriture. Là non plus, ce n'est pas ce qui me pose problème.

    Pour finir, je code en C++11.

  19. #19
    Membre émérite

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Points : 2 328
    Points
    2 328
    Par défaut
    Voici un exemple plus complet, sur lequel je l'espère vous verrez mieux mon objectif.

    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
    26
    27
    28
    29
    30
    31
    32
    template < typename T >
    class Box {
    public:
    		Box(const std::vector<T>& A, const std::vector<T>& B):_A(A),_B(B){};
    		Box(T ax, T ay, T bx, T by):_A(2),_B(2){
    			_A[0]=ax; _A[1]=ay; _B[0]=bx; _B[1]=by; };
    		Box(const Box& box):_A(box.GetA()),_B(box.GetB()){};
     
    		std::vector<T> GetA() const {return _A; };
    		std::vector<T> GetB() const {return _B; };
    		T size(int i) const { return _B[i]-_A[i]; };
    		T sizeX() const { return _B[0]-_A[0]; };
    		T sizeY() const { return _B[1]-_A[1]; };
     
    protected:		
    		std::vector<T> _A,_B;
    };
     
    class Mask {
    public:
        //Mask() {};
        Mask(int sizex, int sizey, int originX=0, int originY=0):_sx(sizex),_sy(sizey),_ox(originX),_oy(originY){};
      	Mask(const Box<int>& box):_sx(box.sizeX()),_sy(box.sizeY()),_ox(box.GetA()[0]),_oy(box.GetA()[1]){};
      	//Mask(const std::vector<int>& A,const std::vector<int>& B){
      	//	Box<int> box(A,B);
      	//	Mask(box);
      	//	};
        ~Mask(){};
     
    protected:
    		int _sx,_sy,_ox,_oy;
    };
    Voila.
    Donc j'ai une classe Box.
    Ensuite je définis une classe Mask. Le constructeur que je souhaite implémenté est commenté.
    Le constructeur de la classe Mask a qui on fourni en paramètres deux std::vector<int>, a besoin de méthode de la classe Box pour pouvoir s'initialiser.
    D'où mon envie de créer un objet Box intermédiaire et d'appeler ensuite le second constructeur...

    Merci à vous

  20. #20
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par lg_53 Voir le message
    3) Pour ce qui est du swap&copy, vous n'avez pas l'air tous d'accord sur la bonne manière de procéder, mais de toute façon si je travaille avec une structure plus complexe qu'un std::vector, ça semble laborieux à mettre en oeuvre.
    Si si on est bien d'accord sur la manière de procéder.
    L'idiome copy & swap rend le code de l'opérateur d'affectation trivial en utilisant le constructeur par copie et swap qui sont "plus faciles" à implémenter. C'est justement lorsqu'il y a plus qu'un std::vector que ça devient interessant.

    Citation Envoyé par lg_53 Voir le message
    4) Pourquoi mettre en protected plutôt qu'en private? C'est un choix personnel. Tout simplement parce que si qqn n'a pas les droits pour modifier la classe mais qu'il souhaite l'améliorer,l'étoffer pour ses propres besoins, et bien il peut faire une classe fllle et a ainsi accès aux attributs librement en lecture et écriture. Là non plus, ce n'est pas ce qui me pose problème.
    Dans ce cas il faut (généralement) mettre le destructeur en public et virtual.
    FAQ C++ - Pourquoi le destructeur d'une classe de base doit être public et virtuel ou protégé et non virtuel ?

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. tableau de classe avec constructeur non copiable
    Par pixelou dans le forum Débuter
    Réponses: 3
    Dernier message: 22/02/2010, 23h09
  2. Système de classe, avec héritage multiple dynamique
    Par kimjoa dans le forum Contribuez
    Réponses: 0
    Dernier message: 09/01/2010, 18h07
  3. class avec constructeur déclarer en static
    Par tims12 dans le forum Langage
    Réponses: 4
    Dernier message: 29/12/2009, 17h58
  4. Héritage d'une classe avec constructeur privé
    Par Braillane dans le forum Langage
    Réponses: 13
    Dernier message: 02/09/2009, 11h59
  5. Réponses: 1
    Dernier message: 28/03/2006, 22h08

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