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

  1. #1
    Expert éminent sénior
    Création d'un tableau d'objets contenant un paramètre au constructeur
    Bonjour à tous

    Habitué du C, je me mets un peu au C++ et je me heurte à des soucis de syntaxe (voire de possibilité)

    J'ai un objet recevant impérativement un paramètre à sa création. Exemple
    Code cpp :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class cXXX {
    public:
    	int n;
    	cXXX(int nn) : n(nn) {}
    };


    Je peux parfaitement créer une instance simple (ie cXXX x(123)) ou bien une instance allouée (ie cXXX *x=new cXXX(456)). Jusque là, ok.

    Maintenant je voudrais créer un tableau d'objets. Et là je me heurte au fait que mon objet reçoit un argument à sa création et je ne sais pas comment passer cet argument.
    J'aimerais bien que ce soit possible de façon intuitive (un truc comme cXXX tab[10](123)) mais bon je ne m'en sors pas (et quelque chose me dit que ce n'est pas possible).
    Je tente l'allocation dynamique avec new mais là aussi mes essais ne fonctionnent pas (ie cXXX *tab=new[10] cXXX(456)).

    Quelqu'un aurait-il la syntaxe qui me manque ?

    Merci
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  2. #2
    Expert éminent
    Essaye ces 2 syntaxes

    Avec un tableau C, mais std::array c'est la même chose (<- à regarder)
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    cXXX tab[3] = {cXXX(123), cXXX(65), cXXX(4520)};



    Avec un tableau C++
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    std::vector<cXXX> tab(10, cXXX(456));

  3. #3
    Rédacteur/Modérateur

    Hello,

    Si tu utilises std::vector, tu peux utiliser std::vector::emplace_back.
    Avec std::array, il faut initialiser ses valeurs directement : std::array<CXX, 3>> arr{CXX(1), CXX(2), CXX(3)};. Même chose pour un tableau C CXX arr[3] = {CXX(1), CXX(2), CXX(3)};.
    Si tu fais un new (mais il ne faudrait plus faire de new !), tu ne dois pas créer un tableau de CXX mais un buffer et utiliser le placement new (et donc tu réécris un cheap vector).
    Sinon, il faut ajouter un constructeur par défaut et une autre manière d'initialiser ton membre, avec une fonction init par exemple.
    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.

  4. #4
    Expert éminent sénior
    Citation Envoyé par foetus Voir le message
    Avec un tableau C, mais std::array c'est la même chose (<- à regarder)
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    cXXX tab[3] = {cXXX(123), cXXX(65), cXXX(4520));
    Oui là je vois (et même je vois que j'aurais dû y penser vu que c'est pareil en C ) mais ça ne peut pas m'aller car la taille du tableau est dans un #define donc je peux pas coder "en dur" le remplissage.

    Citation Envoyé par foetus Voir le message
    Avec un tableau C++
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    std::vector<cXXX> tab(10, cXXX(456));
    Alors là c'est super. Je connaissais pas les vector (enfin je connaissais un peu l'idée) et je vois que ça marche pareil qu'un tableau mais en mieux
    Code c :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class cXXX {
    public:
    	int n;
    	cXXX(int nn) : n(nn) {}
    };
     
    int main() {
    	std::vector<cXXX> tab(5, cXXX(456));
    	for (size_t i=0; i < 5; i++)
    		printf("i=%d, n=%d\n", i, tab[i].n);
    }


    Merci de ton aide C'est exactement ce qu'il me fallait. Juste savoir s'il faut le libérer à la fin (free/delete/destroy/del) ?

    Citation Envoyé par Bousk Voir le message
    Sinon, il faut ajouter un constructeur par défaut et une autre manière d'initialiser ton membre, avec une fonction init par exemple.
    Pour être honnête c'était comme ça que j'avais fait au départ mais bon je suis parti d'un code C que j'ai porté en Python et là c'était simple (tab=tuple(cXXX(123) for i in range(n))) et ensuite j'ai voulu le porter en C++ et là je me suis dit qu'il devait sûrement avoir l'équivalent et que ce serait plus sympa si le paramètre pouvait être passé directement à la création plutôt que tordre mon code pour le remplir après coup.
    Et je suis aussi en train de le porter en php

    Citation Envoyé par Bousk Voir le message
    tu ne dois pas créer un tableau de CXX mais un buffer et utiliser le placement new (et donc tu réécris un cheap vector).
    Je te demande pardon, mais là je suis totalement largué
    Merci à toi aussi
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  5. #5
    Rédacteur/Modérateur

    Tu peux décider de l'adresse utilisée pour créer ton objet avec new.
    https://en.cppreference.com/w/cpp/language/new
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    char buffer[sizeof(CXX)];
    CXX* ptr = new(buffer)CXX(1);
    assert(ptr == buffer);

    Par contre il faut alors appeler le destructeur manuellement.
    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.

  6. #6
    Expert éminent
    Citation Envoyé par Sve@r Voir le message
    Juste savoir s'il faut le libérer à la fin (free/delete/destroy/del) ?
    Non, la collection std::vector s'occupe de toutes les allocations pour toi afin d'agrandir/ de réduire le tableau.
    Tu as la méthode reserve pour lui donner 1 taille sans valeurs (<- 1 lien cplusplus.com en anglais)

    Donc ici , c'est 1 variable locale dans le main.


    Citation Envoyé par Sve@r Voir le message
    Je te demande pardon, mais là je suis totalement largué
    Il parle de surcharger l'opérateur operator new, qui est 1 fonction (<- lien cplusplus.com en anglais)
    En C++, pour 1 classe tu peux redéfinir tous les opérateurs : +, -, <=, ... et donc new.

  7. #7
    Expert éminent sénior
    Citation Envoyé par foetus Voir le message
    En C++, pour 1 classe tu peux redéfinir tous les opérateurs : +, -, <=, ...
    Ouaip, comme en Python (et probablement dans tous les langages objets). Pas de souci.

    Citation Envoyé par foetus Voir le message
    et donc new.
    Noté (quand on y réfléchit on se dit "oui évidemment" mais encore eut-il fallu y réfléchir )

    Bon, pas évident de se mettre à une nouvelle syntaxe
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  8. #8
    Membre expert
    Plutôt qu'une constante avec define, il vaut mieux utiliser une valeur inline constexpr, mais comme cette taille est connue à la compilation, il est toujours possible de construire un tableau remplit:

    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
     
    template<std::size_t... ints, class F>
    auto make_filled_array_impl(std::index_sequence<ints...>, F&& f)
    {
      return std::array{(void(ints), f())...};
    }
     
    template<std::size_t N, class F>
    auto make_filled_array(F&& f)
    {
      return make_filled_array_impl(std::make_index_sequence<N>(), f);
    }
     
    //std::array<cXXX, N> tab = ...
    auto tab = make_filled_array<N>([]{ return cXXX(123); });


    -> https://godbolt.org/z/Lh9cY6


    Citation Envoyé par Sve@r Voir le message

    En C++, pour 1 classe tu peux redéfinir tous les opérateurs : +, -, <=, ...
    Ouaip, comme en Python (et probablement dans tous les langages objets). Pas de souci.
    Non, la surcharge d'opérateur et la POO sont 2 choses différentes. Il y a plein de langage qui ne sont pas objets mais supportent la surcharge d'opérateur, et inversement. Php, Java, Javascript n'ont pas de surcharge des opérateurs mathématique par exemple, simplement parce que cela a rarement du sens en dehors des value-classes.

    Si j'ai 2 classes pour représenter des chaînes de caractère, la première classique et l'autre avec de la couleur, que devrait faire + ?
    - ColoredString + String = ColoredString, mais de quelle couleur va être la seconde partie de la chaîne ?
    - String + ColoredString = String, mais on perd la couleur
    - String + ColoredString = ColoredString, mais il faudra probablement que String connaissent ColoredString et toutes les éventuelles dérivées -> on fait sauter le O de SOLID
    - interdire le mélange, mais le principe de substitution disparaît

    Donc finalement, + ne fonctionnera bien qu'avec les objets du même type ou qu'on n'a préalablement défini, ce qui n'est pas exactement le principe de la POO.
    (faire des classes/objets != POO)

  9. #9
    Expert éminent sénior
    Citation Envoyé par jo_link_noir Voir le message
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    template<std::size_t... ints, class F>
    auto make_filled_array_impl(std::index_sequence<ints...>, F&& f)
    {
      return std::array{(void(ints), f())...};
    }
     
    template<std::size_t N, class F>
    auto make_filled_array(F&& f)
    {
      return make_filled_array_impl(std::make_index_sequence<N>(), f);
    }
     
    //std::array<cXXX, N> tab = ...
    auto tab = make_filled_array<N>([]{ return cXXX(123); });
    Je te demande pardon mais mon niveau actuel fait que je suis totalement largué. Je ne comprends même pas ces "F &&f" (des références de références ? Pourquoi pas vu qu'on peut avoir des pointeurs sur pointeurs avec "**, pourquoi pas le "&&" aussi). Plus tard peut-être

    Citation Envoyé par jo_link_noir Voir le message
    Si j'ai 2 classes pour représenter des chaînes de caractère, la première classique et l'autre avec de la couleur, que devrait faire + ?
    - ColoredString + String = ColoredString, mais de quelle couleur va être la seconde partie de la chaîne ?
    - String + ColoredString = String, mais on perd la couleur
    - String + ColoredString = ColoredString, mais il faudra probablement que String connaissent ColoredString et toutes les éventuelles dérivées -> on fait sauter le O de SOLID
    - interdire le mélange, mais le principe de substitution disparaît
    Je vois bien ton idée. Mais ce n'est pas parce qu'on ne peut pas additionner des pommes et des poires que c'est pas sympa (utile ?) de pouvoir quand-même redéfinir le "+" pour pouvoir additionner des pommes entre elles et pouvoir à côté additionner des poires entre elles.
    Et si un zozo utilise alors cette possibilité du langage pour créer une addition de pommes et poires selon la mélasse qu'il a dans sa tête c'est bien dommage mais c'est pas pour ça que c'est une mauvaise chose. On ne peut pas bannir des possibilités sympas sous prétexte que n'importe quel crétin pourra les utiliser à faire n'importe quoi.

    Citation Envoyé par jo_link_noir Voir le message
    Donc finalement, + ne fonctionnera bien qu'avec les objets du même type ou qu'on n'a préalablement défini, ce qui n'est pas exactement le principe de la POO.
    (faire des classes/objets != POO)
    Hum... si on commence à rentrer dans un débat sur "qu'est-ce que la POO" le topic risque de partir en sucette. Est-ce vraiment utile de pouvoir dire "ce programme A est POO et ce programme B ne l'est pas" ? L'important c'est que pA et pB fonctionnent et soient compréhensibles/evolutifs. Ca me dérangeait pas en C de définir un type "t_cercle" puis une fonction float surfaceCercle(t_cercle *c) puis d'écrire t_cercle c; float s=surfaceCercle(&c). Bon c'était la règle du jeu pour pouvoir ensuite se relire. Mais si maintenant je peux créer une classe "t_cercle" qui contient une méthode "surface" et l'utiliser ainsi t_cercle c; float f=c.surface() je trouve ça plus simple (je ne m'embête plus à chercher des noms pour mes fonctions, je ne risque plus d'écrire t_carre c; float f=surfaceCercle(&c)). Est-ce de la POO ? N'en est-ce pas ? Est-ce réellement important de le savoir ? M. Jourdain faisait de la prose sans le savoir (Le bougeois gentilhomme de Molière) mais justement il ne le savait pas parce que cela ne lui servait absolument à rien de le savoir. Lui, ce qui l'intéressait, c'était de pouvoir parler, tout simplement.
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  10. #10
    Expert confirmé
    Citation Envoyé par Sve@r Voir le message
    Je te demande pardon mais mon niveau actuel fait que je suis totalement largué. Je ne comprends même pas ces "F &&f" (des références de références ? Pourquoi pas vu qu'on peut avoir des pointeurs sur pointeurs avec "**, pourquoi pas le "&&" aussi). Plus tard peut-être
    En fait, comme tu connais la taille de ton tableau, tu peux le "définir directement à la compilation" et ainsi optimiser un peu l'exécution. Et même, tu peux définir tous les tableaux du genre dont la taille est connue à la compilation et c'est ce que fait le code proposé par jo_link_noir. Ceci dit, c'est du C++ avancé et c'est vraiment pas grave si tu ne comprends pas ce genre de truc.