web-dev-qa-db-fra.com

Les modèles de conception C ++ gof dépend fortement du nouveau ou du Shared_ptr

J'essaie d'apprendre les meilleures pratiques pour la conception de code et la réutilisation en C++, donc je passe par le bien connu GOF D Éléments de modèles ESIFS du logiciel réutilisable orienté objet.

J'ai remarqué que presque tous les modèles de conception utilisent des objets alloués de manière dynamique. Pour éviter les fuites de mémoire, je m'appuyais vers l'aide de shared_ptr Pour toutes ces classes. Fondamentalement, le shared_ptr indique généralement une interface de classe abstraite et c'est ainsi que les objets interagissent les uns avec les autres.

Lorsque je cherche une discussion sur Shared_Ptrs pour C++, les gens disent que si vous êtes en utilisant partleed_ptrs partout dans votre code C++, vous êtes Faire probablement des choses mal . Est-ce que ces personnes savent ce qu'ils parlent ou devrions-je collaborer admirement à utiliser une allocation de tas dynamique pour tous mes objets?

Voici un exemple que j'ai trouvé de commentateurs disant à éviter cette pratique de conception: https://stackoverflow.com/questions/25357499/is-it-possible-in-ano-way-c-proprocessor-etc -à-remplacer-partagé-ptrt

Exemple d'interface commune

std::shared_ptr<Inferface> item1(new ConcreteClassA());
std::shared_ptr<Inferface> item2(new ConcreteClassB()); 
item1->Action();
item2->Action();

Exemple d'éviter les interfaces communes et l'allocation de tas

void Action(ConcreteClassA item){
     item.Action();
}
void Action(ConcreteClassB item){
     item.Action();
}

ConcreteClassA item1;
ConcreteClassB item2;
Action(item1);
Action(item2);

Le premier exemple est clairement supérieur à la réutilisation du code, car je n'ai pas besoin d'écrire de nouvelles fonctions pour chaque nouvelle classe, mais cela implique la répartition partagée_ptrs et le tas. C'est un exemple très simplifié de la raison pour laquelle les modèles de conception sont utiles et évidemment, il est beaucoup plus compliqué que cela.

Je suis à un point de confusion sur quelle approche est considérée comme une pratique courante pour la conception de logiciels avec C++. Disons que je fais un logiciel d'application que je devrai entretenir pendant plusieurs années, comme un exemple de rédaction de texte WYSIWYG à partir du livre GOF. Lorsque les gens disent, vous ne devriez pas utiliser de manière approfondie partagée_ptr pour gérer les objets, car cela signifie-t-il que je devrais utiliser d'autres pointeurs intelligents/pointes bruts ou est la pratique courante C++ pour éviter toute allocation de tas dynamique?

Pour moi, OOP a plus de sens pour une langue comme Java, où la collecte des ordures est automatique et les interfaces font partie de la langue.

La pratique de l'industrie est-elle de suivre les modèles de conception à l'aide de l'allocation dyanmique lors du développement d'applications en C++? Si oui, sont des pointeurs intelligents la bonne façon d'y aller?

3
Christian Gabor

Les modèles de conception GOF concernent des moyens intelligents d'utiliser le polymorphisme pour conserver une conception extensible. Par exemple, le modèle de stratégie nous permet de fournir des implémentations de stratégie différentes sans avoir à recompiler aucun code qui utilise la stratégie.

Techniques orientées objet (polymorphisme/envoi dynamique) signifie que lorsque nous avons un objet, nous n'avons pas besoin de savoir que c'est le type actuel. Il devrait être suffisant de connaître son Interface, non si c'est ConcreteClassA ou ConcreteClassB. Vous n'utilisez pas une expédition dynamique lorsque le type d'objet est entièrement connu, par exemple. Lorsque l'objet est stocké par la valeur dans une variable locale. Lorsque vous utilisez une fonction surchargée, elle est résolue lors de la compilation via une expédition statique et ne dispose pas des propriétés d'extensibilité de l'envoi dynamique. Pour plus de détails, veuillez lire mon article Dynamic vs. Dispatch Static .

Lorsque nous connaissons seulement l'interface d'un objet et non son type dynamique, cela nécessite que nous détenons l'objet via un pointeur. Cela pourrait être un pointeur brut, une référence ou un pointeur intelligent. Ces différents types de pointeurs impliquent différentes sémantiques de propriété.

  • Les pointeurs bruts n'impliquent aucune propriété et doivent donc être évités. Devrais-je supprimer ceci ou l'objet juste emprunté? Pas clair. Les pointeurs bruts sont une recette des fuites de mémoire et des segfault. Il est difficile d'écrire un code de sécurité à l'exception avec des pointeurs bruts.
  • Les références signifient que l'objet est temporairement emprunté et appartient à un autre objet. Pratique: une référence ne peut jamais être un pointeur NULL et ne doit pas nécessairement être explicitement déérique.
  • UNE std::unique_ptr Transfère la propriété de l'objet pointu à un objet. Une fois que le pointeur intelligent est détruit automatiquement, l'objet pointé à une autre sera supprimé (→ Raii). Sauf si une référence ou un pointeur partagé est plus approprié, cela devrait être votre type de pointeur par défaut.
  • UNE std::shared_ptr indique la propriété partagée. L'objet pointu à une référence est automatiquement compté par référence. Une fois le dernier pointeur partagé pour référence à cet objet est détruit, l'objet pointu à l'objet est également supprimé. Le comptage de référence implique des frais généraux d'exécution.

Par conséquent, les conseils pour éviter les pointeurs partagés sont corrects. De nombreux graphiques d'objet ont une approche claire et n'ont pas besoin d'une approche de collecte des ordures comme le comptage de référence. Pourtant, si vous en avez besoin, l'option est toujours là. Notez que les pointeurs partagés ne sont pas tout à fait comme la collecte des ordures de style Java, car vous devez toujours éviter les cycles de référence (par exemple en utilisant des pointeurs faibles).

Les modèles GOF envisagent uniquement des techniques orientées objet, mais C++ est beaucoup plus que cela. Notamment, les modèles peuvent parfois aborder des problèmes similaires. Mais les modèles et OOP sont fondamentalement différents. Important, la modification d'un modèle nécessite de recompiler tout le code dépendant, tandis que OOP TECHNIQUES peut isoler les composants.

9
amon

Une meilleure "règle" n'est "pas neuve nue"; Il faut utiliser le pointeur intelligent approprié, le cas échéant:

std :: unique_ptr

utilisé pour des allocations uniques qui ne seront pas partagées sauf par des pointeurs faibles. Un pointeur unique a un seul propriétaire

std :: partagé_ptr

utilisé pour les allocations partagées qui n'ont pas de propriétaire

std :: faible_ptr

utilisé pour les références de non-propriété


De nombreuses utilisations des motifs de conception peuvent être implémentées avec des références faibles_ptr de retour_ptr. Utilisez uniquement un pointeur partagé pour les allocations qui n'ont pas de propriétaire ou survivre à leur créateur.

3
esoterik