Question

Arrière-plan

J'ai une classe de conteneur qui utilise le vecteur < std :: string > intérieurement. J'ai fourni une méthode AddChar (std :: string) à cette classe wrapper qui effectue un push_back () sur le vecteur interne. Dans mon code, je dois ajouter plusieurs éléments au conteneur à un moment donné. Pour cela, je dois utiliser

container.AddChar("First");
container.AddChar("Second");

Cela agrandit le code. Pour simplifier les choses, je prévois de surcharger l'opérateur < < ;. Pour que je puisse écrire

container << "First" << "Second"

et deux éléments seront ajoutés au vecteur sous-jacent.

Voici le code que j'ai utilisé pour cela

class ExtendedVector
{
private:
    vector<string> container;

public:
    friend ExtendedVector& operator<<(ExtendedVector& cont,const std::string str){
        cont.AddChar(str);
        return cont;
    }

    void AddChar(const std::string str)
    {
        container.push_back(str);
    }

    string ToString()
    {
        string output;
        vector<string>::iterator it = container.begin();
        while(it != container.end())
        {
            output += *it;
            ++it;
        }
        return output;
    }
};

Cela fonctionne comme prévu.

Questions

  1. La surcharge de l'opérateur est-elle écrite correctement?
  2. Est-ce une bonne pratique de surcharger les opérateurs dans de telles situations?
  3. Y aura-t-il des problèmes de performances ou d'autres problèmes avec ce code?

Avez-vous des idées?

Modifier

Après avoir entendu les excellents commentaires, j’ai décidé de ne pas surcharger < < comme cela n'a pas de sens ici. J'ai supprimé le code de surcharge de l'opérateur et voici le code final.

class ExtendedVector
{
private:
    vector<string> container;

public:

    ExtendedVector& AddChar(const std::string str)
    {
        container.push_back(str);
        return *this;
    }

         .. other methods
}

Cela me permet d'ajouter

container.AddChar("First").AddChar("Second")

En C #, je peux le faire plus facilement en utilisant le mot-clé params. Le code sera comme

void AddChar(params string[] str)
{
    foreach(string s in str)
       // add to the underlying collection
}

Je sais qu'en C ++, nous pouvons utiliser ... pour spécifier une longueur variable de paramètres. Mais autant que je sache, ce n'est pas sûr. Alors, est-ce une pratique recommandée de le faire? Pour que je puisse écrire

container.AddChar("First","Second")

Merci pour les réponses.

Était-ce utile?

La solution

  

La surcharge de l'opérateur est-elle écrite correctement?

C’est vrai, mais on peut faire mieux. Comme mentionné par quelqu'un d'autre, votre fonction peut être définie entièrement à partir de fonctions publiques existantes. Pourquoi ne pas le faire utiliser seulement ceux-là? À l'heure actuelle, il s'agit d'un ami, ce qui signifie qu'il appartient aux détails de la mise en œuvre. Il en va de même si vous mettez l'opérateur < < en tant que membre de votre classe. Cependant, faites en sorte que votre opérateur < < une fonction non membre , non amie .

class ExtendedVector {
    ...
};

// note, now it is *entirely decoupled* from any private members!
ExtendedVector& operator<<(ExtendedVector& cont, const std::string& str){
    cont.AddChar(str);
    return cont;
}

Si vous changez de classe, vous ne serez pas sûr que votre opérateur < < fonctionnera toujours. Mais si votre opérateur < < dépend entièrement uniquement de fonctions publiques, alors vous pouvez être sûr que cela fonctionnera une fois que des modifications auront été apportées aux détails d'implémentation de votre classe. Yay!

  

Est-ce une bonne pratique de surcharger les opérateurs dans de telles situations?

Comme un autre gars l’a répété, c’est discutable. Dans de nombreuses situations, la surcharge de l'opérateur sera "soignée". à première vue, mais cela ressemblera à l'enfer l'année prochaine, parce que vous ne savez plus ce que vous aviez en tête lorsque vous accordez un amour spécial à certains symboles. Dans le cas de l'opérateur < < ;, je pense que cette utilisation est acceptable. Son utilisation en tant qu'opérateur d'insertion pour les flux est bien connue. Et je connais des applications Qt et KDE qui l'utilisent beaucoup dans des cas comme

QStringList items; 
items << "item1" << "item2";

Un cas similaire est boost.format , qui réutilise également l'opérateur% pour transmettre les arguments des espaces réservés dans sa chaîne:

format("hello %1%, i'm %2% y'old") % "benny" % 21

Bien sûr, il est également discutable de l’utiliser ici. Mais son utilisation pour le format d'impression est bien connue et son utilisation est donc également correcte, à mon humble avis. Mais comme toujours, le style est aussi subjectif alors prenez-le avec un grain de sel:)

  

Comment puis-je accepter des arguments de longueur variable de manière sécurisée?

Eh bien, il existe un moyen d'accepter un vecteur si vous recherchez des arguments homogènes:

void AddChars(std::vector<std::string> const& v) {
    std::vector<std::string>::const_iterator cit =
        v.begin();
    for(;cit != v.begin(); ++cit) {
        AddChar(*cit);
    }
}

Ce n’est pas vraiment confortable de l’adopter. Vous devez construire votre vecteur manuellement puis passer ... Je vois que vous avez déjà le bon sentiment au sujet des fonctions de style vararg. Il ne faut pas les utiliser pour ce type de code et uniquement lors de l’interfaçage avec du code C ou de fonctions de débogage, le cas échéant. Une autre façon de gérer ce cas consiste à appliquer la programmation du préprocesseur. Ceci est un sujet avancé et est assez hacky. L’idée est de générer automatiquement des surcharges jusqu’à une limite supérieure approximativement comme ceci:

#define GEN_OVERLOAD(X) \
void AddChars(GEN_ARGS(X, std::string arg)) { \
    /* now access arg0 ... arg(X-1) */ \
    /* AddChar(arg0); ... AddChar(arg(N-1)); */ \
    GEN_PRINT_ARG1(X, AddChar, arg) \
}

/* call macro with 0, 1, ..., 9 as argument
GEN_PRINT(10, GEN_OVERLOAD)

C'est du pseudo-code. Vous pouvez consulter la bibliothèque de préprocesseurs boost ici .

La prochaine version de C ++ offrira de bien meilleures possibilités. Les listes d’initialisation peuvent être utilisées:

void AddChars(initializer_list<std::string> ilist) {
    // range based for loop
    for(std::string const& s : ilist) {
        AddChar(s);
    }
}

...
AddChars({"hello", "you", "this is fun"});

Il est également possible dans le prochain C ++ de prendre en charge de nombreux arguments (de type mixte) arbitraires à l'aide de modèles variadiques . GCC4.4 sera pris en charge pour eux. GCC 4.3 les soutient déjà partiellement.

Autres conseils

1) Oui, sauf que AddChar est public, il n'y a aucune raison pour qu'il soit un ami .

2) C'est discutable. < < est en quelque sorte dans la position d’être l’opérateur dont la surcharge pour " weird " les choses sont au moins acceptées à contrecoeur.

3) Rien d’évident. Comme toujours, le profilage est votre ami. Vous pouvez envisager de passer les paramètres de chaîne à AddChar et à opérateur < < par référence de const ( const std :: string & amp; ) pour éviter copie inutile.

  

Est-ce une bonne pratique de surcharger   opérateurs dans des situations comme celle-ci?

Je ne pense pas. C'est déroutant pour quelqu'un qui ne sait pas que vous avez surchargé l'opérateur. Tenez-vous-en à des noms de méthodes descriptives et oubliez les caractères supplémentaires que vous tapez, cela ne vaut tout simplement pas la peine. Votre responsable (ou vous-même dans 6 mois) vous en remerciera.

Je préférerais ne pas le surcharger personnellement, car les vecteurs n'ont généralement pas d'opérateur de décalage gauche surchargé - ce n'est pas vraiment un idiome; -)

Je retournerais probablement une référence de AddChar à la place comme ceci:

ExtendedVector& AddChar(const std::string& str) {
    container.push_back(str);
    return *this;
}

afin que vous puissiez ensuite faire

container.AddChar("First").AddChar("Second");

qui n'est pas vraiment beaucoup plus gros que les opérateurs de bitshift.

(voir également le commentaire de Logan sur le passage de chaînes par référence plutôt que par valeur).

Dans ce cas, la surcharge de l'opérateur n'est pas une bonne pratique car elle rend le code moins lisible. Le standard std :: vector ne l’a pas non plus pour pousser des éléments, pour de bonnes raisons.

Si vous craignez que le code de l'appelant ne soit trop long, vous pouvez le prendre en compte à la place de l'opérateur surchargé:

container.AddChar("First").AddChar("Second");

Cela sera possible si vous avez AddChar () renvoyer * this .

C'est drôle que vous ayez cette fonction toString () . Dans ce cas , un opérateur < < à exporter vers un flux serait la norme à utiliser à la place! Donc, si vous voulez utiliser des opérateurs, faites de la fonction toString () un opérateur < < .

L'opérateur n'est pas correctement surchargé ici. Il n’ya aucune raison de faire de l’opérateur un ami car il peut être membre de la classe. Friend est destiné aux fonctions qui ne sont pas des membres réels de la classe (par exemple, en cas de surcharge < < pour ostream afin que l'objet puisse être sorti au niveau de cout ou ofstreams).

Ce que vous voulez réellement que l'opérateur soit:

ExtendedVector& operator<<(const std::string str){
    AddChar(str);
    return *this;
}

Il est généralement considéré comme une mauvaise pratique de surcharger les opérateurs de manière à ce qu’ils fassent quelque chose qu’ils ne le font normalement. < < est normalement un décalage de bits, donc le surcharger de cette façon peut être déroutant. De toute évidence, STL surcharge < < pour "insertion de flux" et ainsi avec cela il pourrait être utile de le surcharger pour votre usage de la même manière. Mais cela ne semble pas être ce que vous faites, alors vous voulez probablement l'éviter.

Il n'y a pas de problème de performances car la surcharge d'opérateur est identique à un appel de fonction normal. Seul l'appel est masqué car il est effectué automatiquement par le compilateur.

Cela va rendre les choses plutôt confuses, j'utiliserais la même syntaxe que std :: cin dans une variable:

std :: cin > > someint;

"Premier" > > conteneur;

De cette façon, il s’agit au moins d’un opérateur d’insertion. Pour moi, quand quelque chose a un < < opérateur surchargé, je m'attends à ce qu'il produise quelque chose. Tout comme std :: cout.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top