Traditionnellement, en C ++, vous créeriez des dépendances dans le constructeur et les supprimeriez dans le destructeur. Cette technique / modèle d'acquisition de ressources, a-t-il un nom commun? < BR>
Je suis tout à fait sûr que j'ai lu quelque part, mais je ne peux pas le trouver maintenant. P> EDIT:
Comme beaucoup l'ont souligné, cette classe est incomplète et devrait vraiment mettre en œuvre un constructeur de copie et un opérateur d'affectation.
À l'origine, je l'ai intentionnellement laissé car il n'était pas pertinent pour la question actuelle: le nom du modèle. Cependant, pour l'exhaustivité et pour encourager les bonnes pratiques, la réponse acceptée est ce qu'elle est. P> p>
3 Réponses :
Raii - L'acquisition de ressources est l'initialisation p>
Bien que typiquement, vous utiliseriez un pointeur intelligent pour faire le Raii pour vous plutôt que de le coder vous-même
Pas nécessairement. L'écriture de la classe Raii vous-même vous donne beaucoup de meilleur contrôle sur la gestion de la vie pour la ressource. Les pointeurs intelligents ne sont que l'un des nombreux exemples possibles de Raii. L'idiome couvre loin i> plus que boost :: partagé_ptr code>
La réponse à votre question est Raii (l'acquisition de ressources est l'initialisation).
mais votre exemple est dangereux: p>
solution 1 Utilisez un pointeur intelligent: P>
class A { public: A(): m_b(new B) {} A(A const& copy): m_b(new B(copy.m_b)) {} A& operator=(A const& copy) { A tmp(copy); swap(tmp); return *this; } ~A() { delete m_b; } void swap(A& dst) throw () { using std::swap; swap(m_b, dst.m_b); } private: B* m_b; };
Pourquoi son exemple fuit-il? Il a alloué sur le constructeur et des interlocates sur destructeurs
En raison du constructeur de copie généré du compilateur.
"Possède un pointeur brut" devrait probablement être étendu à "ou à une poignée de ressource de tout autre type". Il en va de même pour les poignées de fichier, les sockets, les connexions de base de données ou toute autre ressource dont la durée de vie doit être gérée.
BTW, cela montre vraiment le pouvoir de "le pistolet le plus rapide du syndrome de l'ouest". Cette réponse est de beaucoup plus complète et détaillée, mais parce que l'autre est au sommet, il s'agit de tous les upvotes ...
Raii: "Ressource Acquisition B> est l'initialisation"
@jalf Il peut également être que le code affiché est assez dégoûtant et quelque chose que vous ne devriez pas faire sans une bonne explication.
@Pabloariel Le code affiché est assez dégoûtant code> de quelle manière?
En ce que vous utilisez la mémoire dynamique sans raison, vous mettez la partie la plus importante de la classe en bas (le membre contient, qui indique ce que la classe est censée gérer et donc sa raison d'exister), dans ce que vous Utilisez un pointeur brut, en ce sens que cela établit une copie supplémentaire de la valeur de a const & copie code>, dans laquelle il utilise
à l'aide de code> pour "en utilisant" la fonction une seule fois. Il utilise quatre lignes pour un destructeur d'une ligne ... Comme je l'ai mentionné, cette classe peut être écrite comme
classe A {b m_b = {}; } code> et il devrait sauf s'il y a une bonne explication pour le faire d'une autre manière.
Il y a une myriade de bons exemples de Raii, tels que les gardes de verrouillage, les pointeurs intelligents COM +, les pointeurs intelligents en général (tho the STD :: Les implémentations sont discutables à tout le moins), des mappages de fichiers et ainsi de suite. Cependant, il n'y a aucune raison de faire quelque chose comme dans votre exemple, d'envelopper NOUVEAU () et de Supprimer () sans raison. Pas un exemple du monde réel et dangereux d'imiter.
@Pabloariel: Merci pour l'entrée. Je suis d'accord avec pas un exemple du monde réel code>. Mais les détails devaient être donnés pour répondre à une réponse adéquate à la question initiale. Personnellement en désaccord avec tous vos autres points.
@Pabloariel Compte tenu de la question originale une copie doit être une copie vraie. Ainsi, ni std :: unique_ptr code> ou
std :: partagé_ptr code> fonctionnerait réellement (sans code supplémentaire). unique_ptr empêcherait simplement la copie et Shared_Ptr ne vous donne pas de nouvelles données de copie mais partagées. Maintenant, je peux voir utiliser
b m_b code> est définitivement meilleur que d'utiliser
b * m_b code> dans la plupart des cas b>, mais l'OP a choisi une implémentation qui utilise un pointeur utilisant un pointeur Pour une raison (sinon la solution plus simple aurait été utilisée). Donc, dire
classe A {b m_b = {};} code> n'est pas une réponse.
@Pabloariel Compréhension Comment envelopper Nouveau / Supprimer est une bonne idée pour tous les programmeurs. Bien que le meilleur conseil soit toujours avec STD :: "Pointeur intelligent" comme suggéré avec le premier exemple.
@Pabloariel basé sur vos commentaires, j'ai le sentiment que vous n'avez pas non pas découvert la copie et échanger des idioms, je vous suggère de faire des recherches à ce sujet.
@Pabloariel basé sur vos commentaires J'ai le sentiment que vous ne comprenez pas comment écrire une standard swap () code> méthode. Vous devriez probablement apprendre le moyen idiomatique d'écrire un échange.
Je sais à propos de la copie et de swap idiom. Je sais aussi comment éviter de copier des données redondantes et des allocations de mémoire inutiles. Je ne pense pas qu'il y ait jamais la nécessité d'utiliser "nouveau" dans un constructeur de nos jours, mais vous êtes libre de fournir un exemple du monde réel si vous pensez qu'il y a des moments inévitables.
@Pabloariel Qu'en est-il de std :: vecteur <> code> je parie qu'il a une nouvelle dans son constructeur :-). Fondamentalement, chaque fois que vous créez un conteneur ou un pointeur intelligent, ils sont nécessaires. Juste parce que la bibliothèque standard a les plus courantes ne signifie pas que vous ne rencontrerez jamais des situations où les standards ne suffisent pas (vous devez donc savoir comment le faire lorsque vous devez). Bien que celles-ci soit très rare. Tout le monde devrait essayer de créer un conteneur / pointeur intelligent (pas seulement pour l'expérience d'apprentissage, mais aussi de comprendre à quel point ils doivent bien faire correctement).
@Pabloariel maintenant, je serai le premier à dire que, dans moderne C ++, vous ne devriez pas utiliser de nouveau / Supprimer (surtout lorsque vous parlez aux débutants). Mais quand vous êtes dans la vraie vie et que vous avez l'expérience, ces choses se posent et que vous devriez savoir comment les utiliser. La déclaration Je ne pense pas qu'il y ait jamais la nécessité d'utiliser "nouveau" dans un constructeur de nos jours code> est juste mal. Bien sûr, il est rare mais ne comprenant pas comment le faire lorsque vous êtes expérimenté est un problème. Ne le faites pas quand vous avez inexpérimenté mais la vie réelle est beaucoup plus compliquée et ainsi que l'utilisateur expérimenté devra le savoir.
Ok, ils devront peut-être savoir qu'il existe et que les gens font de telles choses, mais ils ont également besoin de savoir que ce n'est pas la façon dont ils devraient le faire. Même quelque chose comme std :: vecteur code> ne doit pas avoir d'attributions dans son constructeur par défaut, uniquement dans les opérations de redimensionner () telles que les constructeurs de copies, mais c'est un type de constructeur différent. Même alors, j'utilise
malloc () code> et de placement neuf afin d'allouer la mémoire supérieure à celle demandée pour plus rapide
push () code>, et il n'y a pas de correspondance
supprimer code> s pour un tel
nouveau code> s.
@Pabloariel Vous devez étudier plus si vous utilisez malloc () code> !!!! (C'est un système d'allocation de mémoire d'une langue complètement différente). Oui
std :: vecteur <> code> a besoin d'une allocation dans le constructeur par défaut et d'une distribution dans le destructeur !!!!! S'il vous plaît lire plus sur Raii !!!!! Mais maintenant, vous êtes aussi b> Modification de la discussion à l'affectation des éléments dans le conteneur, c'est sûr qu'ils ont besoin d'utiliser le placement neuf et vous obligent également à appeler manuellement le destructeur. mais b> qui n'a rien à voir avec cette discussion. Pourquoi essayez-vous de construire un homme de paille?
Mais aussi, ils ont besoin de savoir que ce n'est pas la façon dont ils devraient le faire. Code> C'est une déclaration globale très globale. Vous devez savoir comment le faire, car de temps en temps, vous B> doit le faire. Maintenant, la plupart du temps, vous n'aurez pas besoin de le faire (et quand vous le feriez, je vous attendrais à ce que mes développeurs justifient). Mais la vraie vie n'est pas aussi simple que vous semblez penser que les choses ne sont pas aussi simples et coupées et séchées que vous semblez penser. Vous devez savoir comment faire parce que c'est quelque chose que vous utiliserez.
Je n'ai jamais vu une situation dans laquelle vous devez le faire et que j'ai beaucoup de développements à titre d'exemple, des services Web aux jeux 3D disponibles sur GitHub. De plus, MALLOC () CODE> fait toujours partie de C ++ ( en.cpprefreence.com/w/cpp/memory/c/malloc ), et vous devez l'utiliser afin de tirer parti de la placement nouvelle qui permettent des optimisations importantes, bien que je n'utilise même pas cela comme je avoir une enveloppe pour appliquer malloc aligné dans les différentes plates-formes. Mon code contient des pointeurs intelligents personnalisés et des classes STD :: Vecteur, mais le constructeur par défaut n'initialise que les membres à zéro / null.
@Pabloariel Vous n'avez pas besoin de l'utiliser pour utiliser le placement neuf. Vous pouvez utiliser les allocateurs C ++. MALLOC CODE> fait partie de C. Lisez la page que vous avez liée: elle utilise même une zone d'allocation de mémoire potentiellement différente. Il arrive simplement qu'il existe des liaisons de C ++ au langage C (comme il existe des fixations de presque toutes les langues à c).
@Pabloariel Désolé C'est un argument classique de l'incrédulité: Je n'ai jamais vu une situation dans laquelle vous devez le faire code>. Je le faisais depuis longtemps. RAII B> est le mécanisme principal que les développeurs C ++ doivent apprendre. J'ai écrit plusieurs articles sur la façon de procéder correctement: voir: lokiastari.com/series la section des vecteurs et des pointeurs intelligents où je me promène correctement. Vous devriez certainement apprendre à le faire. Ceci est une compétence dont vous aurez besoin si vous souhaitez continuer en tant que développeur C ++.
Je sais comment faire Raii. Je l'utilise quand il est logique, par exemple pour les verrous et les poignées de fichiers mutex. Rien que vous ne puissiez jamais nouveau () dans le constructeur par défaut, cependant. Mon cadre inclut ses propres implémentations de pointeur intelligent et d'autres installations RAII et si vous avez des critiques, ils seront plus que les bienvenus. En outre, l'utilisation d'en-têtes C conduit à des constructions plus rapides que de compiler des fichiers de modèle C ++.
@Pabloariel Raii est pour tout ce qu'il y a Créer / DosomThing / Destory code> motif. Ils sont fondamentalement l'idiome standard en C ++ et utilisé pour tout. Si vous n'écrivez pas des destructeurs, vous faites probablement quelque chose de mal. C Les en-têtes ne sont pas géniaux car ils sont incompatibles s'ils mettent les choses dans l'espace de noms STD. C Files d'en-tête C => Toujours espace de noms global (parfois espace de noms STD). C ++ Header Files => Toujours Espace de noms STD. Si vous souhaitez échanger la vitesse lors de la compilation de la sécurité, c'est une fausse économie à mesure que le coût du débogage des problèmes obtenus sera des ordres de grandeur plus grand
Ps. Pourquoi utiliseriez-vous RAII pour Mutex Serrures STD :: LOCK_GUARD CODE> et FICHIES> STD :: FRSTREAM CODE> Mais pas pour la mémoire
std :: vecteur code>, quand tout d'entre eux sont déjà en standard? Y a-t-il quelque chose de magique sur la mémoire et non d'autres ressources?
Je pense qu'aucune de ces classes ne nouveau () code> dans leur constructeur par défaut et ils ne devraient pas s'ils souhaitent éviter de nombreuses allocations inutiles, mais vous êtes libre de rechercher le code et de les partager ainsi Tout le monde peut voir comment je me trompe. c'est à dire. avec un exemple de monde réel.
Pas de soucis sur le débogage. Il est résolu en utilisant mes propres implémentations de choses comme STD :: Vector. Je n'utilise jamais de tableaux nus ou de points cru.
Cette technique est surtout connu comme RAII - allocation des ressources est Initialisation strong>. Il a sa propre balise sur ce site. P>
Alternative. Les noms plus intuitives ont été proposées, notamment: p>
Bad Classe A. Copie Constructor et Opérateur d'affectation sont manquants.
Une solution plus simple si la copie / affectation n'est pas nécessaire consiste à dériver de Boost :: Non géré. Vous permet d'économiser de définir ces deux fonctions. (et assure qu'ils ne sont pas invités accidentellement)
Quelle utilisation inutile de la mémoire dynamique. Quelle terrible design et formatage de la classe. Quel est un exemple terrible de C ++ dans l'ensemble. Pourquoi voudriez-vous jamais faire ça? Pourquoi voudriez-vous jamais utiliser de nouveaux et supprimez-vous de toute façon et de manière aussi inutile. Juste faire
classe A {b m_b = {}; } code> et vous obtenez à peu près la même chose, mais sans pointeurs à risque et utiliser moins de 1 / 4ème du code.
Vous n'avez pas besoin d'utiliser
privé: code> parce que l'ensemble du point du mot clé code> est de définir les membres privés comme défaut, car ils sont censés être au-dessus de la classe, et non cachée en bas dans un endroit où vous devez faire défiler la page entière pour les voir. Au cours de mes 30 années de programmation, je n'ai jamais vu un seul argument pour mettre les membres en bas et que la langue est clairement destinée à les avoir au sommet.