2017-04-20 1 views
3

J'écris une classe qui représente un bouton. Ce bouton peut avoir ou non divers attributs tels qu'un texte écrit dessus, un raccourci, une texture ou un remplissage de couleur plat. Donc, si par exemple, ce bouton n'a aucun ensemble de texture, le processus de dessin de texture est ignoré. Ma première solution a été d'utiliser des valeurs par défaut inventées qui indiqueraient que l'attribut donné est inutilisé (si la valeur alpha de la couleur était 0, le dessin de remplissage de couleur serait ignoré etc.).Utilisation des valeurs std :: optionnel vs "inutilisé/par défaut"

L'autre option que j'ai est d'utiliser std :: optionnel nouvellement ajouté qui serait beaucoup plus clair et plus simple à utiliser.

Voici les 2 exemples cités:

class Button { 
    void draw() { 
     if (fill) 
      drawRectangle(*fill); 
     if (sprite) 
      drawSprite(*sprite); 
     if (font) 
      drawText(name, *font); 
    } 

    std::optional<std::string> font; 
    std::optional<std::string> sprite; 
    std::optional<Color> fill; 
} 

class Button { 
    void draw() { 
     if (fill.alpha != 0) 
      drawRectangle(fill); 
     if (sprite != "") 
      drawSprite(sprite); 
     if (font != "") 
      drawText(name, font); 
    } 

    std::string font; 
    std::string sprite; 
    Color fill; 
} 

Que peuvent les avantages et l'utilisation disadvatages std :: en option dans ce cas? Ce qui m'intéresse principalement, ce sont l'utilisation de la mémoire et les différences de frais généraux.

Devrais-je simplement, au lieu d'utiliser si pour vérifier si facultatif contient une valeur, appeler la valeur() et attraper l'exception?

+3

En ce qui concerne votre question sur l'utilisation de '.value()' et d'attraper l'exception au lieu de vérifier si elle a une valeur: C'est une mauvaise idée. Comme son nom l'indique, les exceptions sont exceptionnelles, et le surcoût causé par le déroulement de la pile n'est pas négligeable. En supposant que beaucoup de boutons, par ex. n'a pas de couleur, l'option étant vide n'est pas un cas exceptionnel du tout. – Corristo

+0

Je ne vois pas 'std :: optionnel ' comme extrêmement utile, à moins qu'une chaîne vide ne soit différente d'aucune chaîne. –

+0

'std :: optional' était destiné aux retours de fonctions, pas aux membres de la classe. C'est une chose très inutile à faire. Notez également que 'sprite.empty()' devrait être utilisé sur 'sprite ==" "'. – Pubby

Répondre

4

En ce qui concerne les frais généraux, c'est surtout sous la forme d'espace. optional prend toujours tout ce qu'il stocke, plus un booléen, plus tout rembourrage supplémentaire pour faire fonctionner l'alignement. Par exemple, std::string est souvent implémenté en 24 octets avec un alignement de 8 octets. Un optional<string> serait alors de 25 octets, mais en raison de l'alignement, il finirait par être de 32 octets. Pour un type primitif (int, ou enum), il faudra généralement doubler le stockage nécessaire de 4 à 8 octets, ou quelque chose de similaire.

En ce qui concerne les performances, dans ce cas, il n'y aura aucune différence en dehors des effets de cache (si l'optimiseur est intelligent). Comparer un std::string à un littéral de chaîne vide sera probablement optimisé pour un appel à std::string::empty (vous devriez probablement l'écrire de cette façon), qui vérifie simplement si un entier est zéro, ce qui est la même chose que votre comparaison pour Color, qui est essentiellement la même chose que vérifier si un booléen est nul.

Cela dit, j'aime optional; Je pense qu'il communique plus clairement l'intention du code. Cependant, si vous avez des valeurs sentinelles très évidentes, alors peut-être que c'est moins précieux.

Dans certains cas, vous pouvez avoir votre gâteau et le manger aussi avec compact en option: https://github.com/akrzemi1/compact_optional. Il a l'interface externe identique à un optionnel régulier, mais vous lui donnez une valeur sentinelle et il utilise cette sentinelle pour stocker l'état manquant. Peut ne pas fonctionner correctement avec toutes les classes cependant.

+3

+1. Les principaux points à retenir sont: "** communique plus clairement l'intention **" et "** si vous avez des valeurs sentinelles très évidentes, alors peut-être est-il moins précieux". "Communiquer clairement l'intention a priorité sur tout sauf" facile à lire "et" difficile d'abuser " – sp2danny

1

Bien que std :: optionnel puisse supporter le surcoût d'une valeur booléenne supplémentaire, il a aussi un but descriptif ici: il exprime pleinement le concept que vous essayez de mettre en code, ce qui fait que ce code montre directement au lecteur qui se passe. Parce que c'est l'interface utilisateur, et les frais généraux booléens est relativement faible, je dirais aller le chercher.

Je conteste l'affirmation que std :: optionnel était destiné uniquement aux retours de fonction. Ce serait ridiculement limité.