12
votes

Comment éviter les pointeurs lors de l'utilisation de polymorphisme dynamique?

Pendant longtemps maintenant, j'ai perçu des pointeurs, Nouveau et Supprimer un peu inutile en C ++, sauf convention d'objets de longue durée, les références étant une alternative plus propre que convient mieux au modèle Raii. Cependant, je suis toujours incapable de déterminer comment éviter les pointeurs lors de l'utilisation de polymorphisme dynamique en C ++.

SUPOSE que nous avons ces clasières: xxx

peut être transmis à invoquer comme référence et il n'y a pas de pointeurs impliqués (bien, du moins du point de vue du programmeur). Cependant, l'exemple ci-dessus est essentiellement un polymorphisme statique.

Si je voulais faire le type de B dépendant de quelque chose d'autre, je devrais utiliser des pointeurs: < Pré> XXX

Ceci a l'air laids comme enfer pour moi. Bien sûr, je pourrais utiliser des pointeurs intelligents: xxx

mais les pointeurs intelligents ne sont que des emballages pour les pointeurs.

Je voudrais savoir s'il ya un moyen de Évitez cela et utilisez des classes polymorphes sans utiliser de pointeurs.


6 commentaires

Si (COND) INVOKE (B (...)) Invoque (C (...)) , aka style fonctionnel? Ou en C ++ 11 avec Lambdas si les parties communes sont grandes et que vous ne voulez pas de fonction globale supplémentaire pour cela.


@Xeo Cela fonctionnerait dans cet exemple spécifique, mais cela n'est pas toujours applicable.


Ce que vous appelez un polymorphisme statique n'est pas du tout un polymorphisme statique. invoke ne connaît pas précisément le type de son argument. Vous seulement Pensez C'est un polymorphisme statique car vous pouvez voir le code et le type étant passé!


@Hbcdev je suis d'accord. Mais cela se comporte exactement comme tel et peut même être optimisé par le compilateur pour ne pas utiliser l'appel de la fonction virtuelle du tout.


@Tibor: Cela ne fonctionne pas dans les cas où vous avez besoin de stocker de manière conditionnelle un objet ou l'autre, true. Dans ces cas, vous n'allongerez pas d'utiliser des pointeurs, car ils sont essentiels au polymorphisme dynamique.


@HBCDEV: Je pense que ce que SOI signifie exclure tous les cas où la liaison dynamique pourrait être remplacée par des métaprogrammations car toutes les décisions basées sur le type de temps d'exécution peuvent être connues au moment de la compilation. La seconde Le type réel d'un objet dépend des informations d'exécution, vous perdez certaines options d'optimisation. Donc - si quelque chose est un consexpr , vous pourrait appeler ce "polymorphisme statique" de manière à parler.


6 Réponses :


1
votes

Je ne sais pas pourquoi vous voulez éviter les pointeurs de tout type de coût. Il est absolument bon d'utiliser std :: unique_ptr / std :: partagé_ptr (quand il y a un besoin, bien sûr). Les pointeurs intelligents ne sont pas des "wrappers" autour des pointeurs. Ils vous ont laissé choisir entre différentes sémantiques et avoir des utilisations différentes.


0 commentaires

4
votes

Compte tenu de votre dernier exemple, vous pouvez éviter l'utilisation des pointeurs (bien que je ne voie pas vraiment de gros problème avec des pointeurs intelligents) xxx

ceci vous permet d'utiliser le titulaire comme-si C'était l'objet réel: xxx


2 commentaires

Bien sûr, cela fonctionnerait. Ma question était toutefois davantage sur l'utilisation de l'utilisation de Nouveau et * du tout.


@Tibor: Eh bien, le qualificatif de type * ainsi que l'opérateur * sont cachés à l'intérieur de cette installation (qui peut facilement être transformé en un modèle générique ), tout comme les pointeurs intelligents cachent leur utilisation des pointeurs bruts.



1
votes

Le cas d'utilisation que vous pensez probablement est celui d'une sorte de usine , que vous pouvez servir parfaitement bien avec un pointeur intelligent et aucun nouveau du tout : xxx

utilisation: xxx

(vous devez avoir une définition de make_unique quelque part, qui est malheureusement manquant dans la bibliothèque standard actuelle mais sera modifiée éventuellement.)


3 commentaires

La définition de make_unique contient un appel à nouveau cependant. Fondamentalement, vous ne pouvez pas vous échapper des pointeurs pour résoudre ce problème, bien que vous puissiez créer une variété d'abstractions jolies et utiles.


+1 Pour me faire comprendre que j'ai oublié les routines _ (et utilisé sale OL ' nouveau ).


@Matthewwalton: Oui, bien sûr. Et chaque conteneur dynamique dans l'appel de bibliothèque standard appelle :: nouveau gauche droite et centre. Mais le point ici est d'établir un nouveau nouveau - et idiom sans pointeur pour C ++! Et gardez à l'esprit la différence de sécurité d'exception qu'une fonction wrapper fait.



1
votes

J'aimerais savoir s'il existe un moyen d'éviter cela et d'utiliser des classes polymorphes sans utiliser de pointeurs.

Non, voici comment le C ++ a été conçu en premier lieu. Pour éviter de trancher, vous devez utiliser des pointeurs ou des références.


0 commentaires

7
votes

Vous ne pouvez pas éviter les pointeurs pour cela. Si vous ne les aimez pas, C ++ ne sera pas la langue pour vous, car si vous voulez faire quelque chose de polymorphe, vous devrez utiliser des pointeurs pour passer les utilisations les plus triviales. Construire des objets sur le tas, c'est-à-dire à l'aide de Nouveau correspond à la manière dont vous évitez la durée de vie cachée des objets construits de pile, que vous devez faire si vous souhaitez faire des choses à l'intérieur des branches conditionnelles, puis les assigner à des variables dans Une portée parentale - Si vous n'avez pas besoin de faire cela, vous n'avez pas non plus besoin de polymorphisme car vos types sont tous déterminables au moment de la compilation. Il n'y a aucun moyen de cela.

Utilisez des pointeurs intelligents bien sûr, ils peuvent vraiment aider à éviter les problèmes avec des cycle de vie du pointeur, mais il y aura des pointeurs y compris quelque part quelles que soient les abstractions vraiment cool que vous finissez par utiliser.


1 commentaires

Plus j'y pense, plus cela fait de sens. Si les pointeurs n'étaient pas nécessaires, il faudrait avoir d'autres contraintes sérieuses aux types polymorphes pour éviter la tranchée, car Bјовић a souligné.



0
votes

Il n'y a aucun moyen si vous avez besoin de créer des objets de manière dynamique et de les persister de la portée de la fonction. Le mieux que vous puissiez faire ici - masquer la création d'objets dans des pointeurs d'usine et intelligents.

Vous pouvez également envisager des objets statiques dans la fonction de fonction, mais ce n'est pas une bonne solution - vous ne pouvez créer que le nombre limité d'objets de cette façon: P >

template <class T, int Instance> T& create()
{
   static T instance;
   return instance;
}

MyClass& obj1 = create<MyClass, 1>();
MyClass& obj2 = create<MyClass, 2>(); 
MyClass& obj3 = create<MyClass, 3>(); 
// etc


3 commentaires

Les singletons sont généralement laids et, dans ce cas, particulièrement dangereux. C'est effectivement un générateur pour les objets d'Amost-Singleton.


@bitmask bien sûr, et c'est parce que j'ai écrit cela est possible, mais pas une bonne solution. Notez que réellement vous peut créer beaucoup d'objets de même type, il ne s'agit donc pas de singletons ...


Et que soit pourquoi je n'ai pas voté par vote. La personne qui n'est pas non plus acceptée avec mon précédent commentaire.