6
votes

Pour une fonction surchargée, appelez la version spécialisée pour les instances de parents et d'enfants

J'ai demandé à un Question plus tôt mais il s'avère Mon problème n'a pas été correctement modélisé par mon exemple. Donc, voici mon problème réel:

  1. J'ai la classe A et la classe b hériter de A ,
  2. J'ai deux fonctions foo (a &) et foo (B &) ,
  3. J'ai une liste des pointeurs A * , contenant des instances de A et b .
  4. Comment puis-je arriver à appeler foo (a &) pour les instances de A et foo (B &) pour des instances de B ? Contraintes: Je peux modifier A et B Mise en œuvre, mais pas FOO de la mise en œuvre de "

    voir ci-dessous un exemple: xxx

    Bien que j'utilise un conteneur avec des pointeurs, bar est appelé objet de la Classe des parents, pas la classe enfant: xxx

    Je m'attendais à xxx

    pointeurs de passage à bar (en réécrivant sa signature) n'aide pas.

    thx à Antonio pour aider à clarifier la question.


19 commentaires

Le type si ** IT est A , alors pourquoi vous attendiez-vous "Ceci est un B"?


Faire à la place la méthode virtuelle A :: bar () et b :: bar (bar () et vous verrez ce que vous attendez.


Parce que je vais allouer A B de en elle.


Peu importe. Comment la résolution de surcharge de surcharge peut-elle savoir qu'un pointeur à un a indique réellement un B ? La résolution de la surcharge arrive à la compilation.


@ Jarod42 C'était ma réponse, mais les fonctions ne sont pas membres =). OP aurait besoin de lancer A à B .


Le bar n'est pas membre de ma classe dans mon problème.


@Lukeskywalker: Si vous souhaitez que la surcharge fonctionne correctement sur les pointeurs de classe de base, vous devez utiliser des fonctions de membre virtuel.


Andyg: oui mais ce n'est pas une option pour moi ici. Dans mon problème du monde réel, la barre est une méthode d'une autre classe. Peut-être que j'ai besoin d'utiliser une méthode de proxy virtuelle pour appeler la barre ...


@LUKESKYWALKER: Vous devez donc avoir un polymorphisme et non autorisé à créer une fonction de membre virtuel?


Andyg: Oui, je peux créer des membres virtuels, mais à bien, le code que je dois exécuter n'est pas dans ma classe. Mais en utilisant votre proposition, je peux toujours créer une méthode de membre virtuel FOO qui apportera une barre d'appel. Cela pourrait être une solution à mon problème, bien que ce soit un peu compliqué ...


A n'est pas une classe polymorphe, il n'ya donc aucun moyen d'accéder à la partie B de quoi que ce soit via la liste et que vous ne pouvez également supprimer les pointeurs de la liste. (Eh bien, vous pouvez STATIC_CAST Toutefois, cela provoque UB si vous le faites sur quelque chose qui n'est pas un B et que vous n'avez aucun moyen de savoir dans le code général).


@LUKESKYWALKER La fonction ne doit pas nécessairement être dans votre classe mais votre classe dérivée doit remplacer quelque chose de votre classe de base. Ensuite, vous pouvez utiliser dynamic_cast à DownIntInt A à B. J'ai ajouté la réponse.


@Lukeskywalker Je crois que cela pourrait être une très bonne question (= précieux pour les futurs utilisateurs) si vous faites simplement un peu plus clair votre question sur ce que vous voulez exactement, et pourquoi vous le souhaitez de cette façon. En d'autres termes, en d'autres termes, comme la question est posée maintenant, de nombreuses réponses ci-dessous sont correctes, mais vous cherchiez exactement à la réponse de Dasblinkenlight.


@Antonio j'ai précisé ma question. Faites-moi savoir si ce n'est pas clair.


@LUKESKYWALKER Je ne comprends pas comment vous pouvez utiliser Dasblinkenlight Réponse et garder la barre () en tant que méthode d'une autre classe: Comment appeleriez-vous cette méthode de A et B, comme requis par sa solution?


@antonio: Parce que j'ai une poignée de la barre de maintien de la classe dans A. A (et B) sont réellement observables et ma fonction de barre est la méthode de notification de l'observateur.


@Lukeskywalker J'apprécie vos efforts pour réduire votre problème réel à un échantillon de code minimal, mais je pense que l'ajout de cette classe Oberver à la photo aurait aidé à mieux comprendre votre problème.


@antonio: Désolé si le problème n'était pas clair initialement. La chose est le problème que je mentionne est un problème général et d'introduire plus de détails fonctionnels peut vous avoir guidé pour offrir une conception alternative qui ne m'aurait pas aidé. Connaissant cela maintenant, quelle serait votre proposition?


Laissez-nous Continuez cette discussion en chat .


5 Réponses :


2
votes

Vous recherchez un polymorphisme à temps d'exécution. Ceci est pris en charge "naturellement" pour les méthodes de membre virtuel.

Une alternative serait d'utiliser RTTI et Cast a * à B * et appelez bar sur le succès ... ou static_cast Si vous êtes vraiment sûr qu'il y ait b * objets. Généralement besoin de descendre indique une conception problématique.

Note importante : Check-temps d'exécution dans dynamic_cast exige que le type soit polymorphe de toute façon. Peut-être que votre A répond à cela, mais vous ne pouvez tout simplement pas changer la classe. Sinon, static_cast est la seule option disponible.

Si vous avez le contrôle sur la classe, vous pouvez utiliser le polymorphisme standard et les mécanismes de surcharge à l'aide de méthodes virtuelles sur ce En tant que façade pour l'appel "externe": xxx

Ceci a également des inconvénients. Les déclarations avant sont nécessaires. Et vous devez prendre soin de tout, tout est défini correctement, car si vous oubliez la ligne // important Le compilateur prendra la définition de external_bar pour A & Comme il est implicitement convertible et que vous obtiendrez peut-être un mal de tête repérer l'erreur.


2 commentaires

Sonnent honnêtement comme ceci est l'itinéraire que OP descendra. Il y a toujours la solution laidienne pour utiliser une union Typeafe comme boost :: variante


Une autre option est indirectement fournie par andyg. Utilisez une méthode de membre virtuel qui appellerait la barre. Essayé et ça marche, mais je ne peux pas poster du code dans un commentaire.



4
votes

edit: Cette réponse La première version de la question, voir maintenant Solution de DasblinkLight .


si vous faites: xxx

alors * b sera de type A. C'est ce que vous faites dans votre cycle. Il n'y a pas de "virtualité" ou de polimorfisme impliqué dans cela.

Le code suivant donne le comportement que vous recherchez: xxx

prenant mon exemple ci-dessus, dans Ce cas: xxx

imprimera ceci est ab .


11 commentaires

OP demande une solution pour une fonction non membre, et il semble que ce ne soit pas une question XY.


@ Luk32 Je crois que ma réponse aborde la question ici. Voyons comment opt ​​réagit.


Antonio, la réponse que j'accepterais serait si la barre reste une fonction en dehors des classes et s'appelle par une méthode de membre virtuel OT A et B (appelée FOO par exemple) qui serait elle-même barre.


@Antonio sûr, si nous sommes autorisés à changer de signature de classe. C'est la bonne façon.


Oui, j'ai le contrôle de la classe, pas sur la barre de fonction.


@Lukeskywalker Si vous avez le contrôle de la manière dont la classe est écrit, faites une fonction de barre qui utilise le pointeur pour appeler la méthode de la barre. À savoir, prenez mon code et modifiez votre référence FOO définition de la fonction sur Barre de void (A * a) {A-> bar ();}


Oui, c'est ce que je mentionne 2 commentaires ci-dessus :)


@Lukeskywalker Je crois ce que vous avez dit, c'est différent. Dans la méthode que je propose, la méthode virtuelle n'appelle pas la fonction externe, l'opposé est vrai.


Notez qu'il est possible de déléguer une fonction libre; par exemple. La barre librets () peut rester aussi opérationnelle que l'OP, et la fonction virtuelle serait Vide virtuel bar_helper () {bar (* ceci); } , vous auriez besoin d'écrire cela dans chaque classe (vous pouvez dériver via CRTP pour le faire paraître un peu de taille)


Antonio: J'ai édité votre réponse avec ce que je crois que c'est le plus proche de ce qui pourrait être atteint avec un design simple et suffisamment propre.


@Lukeskywalker Je vous suggère de l'ajouter à votre question. J'ai vu la modification, je pense que cela dévie avec l'intention de ma réponse.



1
votes

D'autres ont déjà expliqué comment il peut être atteint.

Je vais juste me limiter à la raison pour laquelle c'est le cas. P>

B devient implicitement jeté à un ici. Donc, il n'a actuellement que des propriétés d'un. P>

La coulée ascendante est implicite en C ++. P>

Downcasting en C ++ n'est possible que si votre classe de base est polymorphe. P>

en courte nécessité polymorphe n'est que quelque chose dans votre classe de base pouvant être remplacée par votre dérivée !! Méthodes virtuelles forts> em> p>

alors vous pouvez utiliser RTTI et dynamic_cast comme prescrit par d'autres personnes pour le faire. P>

Exemple: P>

p>

#include <iostream>
#include <list>

class A {
public:
virtual void dummy() = 0;
};

class B : public A {
public:
void dummy() { }
};

void bar(A &a) { std::cout << "This is an A" <<  std::endl; }
void bar(B &b) { std::cout << "This is a B" << std::endl; }

int main(int argc, char **argv) {
  std::list<A *> l;
  l.push_back(new B());
  l.push_back(new B());

//Prints A
  for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
    bar(**it); 

//Prints B
  for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
    bar(dynamic_cast<B&>(**it));
}


Answer:
This is an A
This is an A
This is a B
This is a B


4 commentaires

La boucle imprime B lancera une exception pour tous les éléments non-B de la liste; Je suppose que ce code est censé être extensible de la liste contenant différents types d'articles


Vrai. Si ce sera la liste des objets de AnyType d'A, il doit alors avoir une fonction virtuelle à la base qui sera remplacée par les enfants suivants. semblable à la réponse.


@Mattmcnabb Je viens d'essayer d'expliquer Down Downcast et implicite avec ceci.


Pour être complètement correct static_cast permet également de casser la mise en couteau, mais il court avec des lacets de chaussures non liés.



0
votes

Antonio a écrit une bonne solution impliquant des fonctions virtuelles. Si vous ne voulez vraiment pas utiliser une fonction virtuelle pour une raison quelconque, vous pouvez utiliser dynamic_cast code> dans une fonction libre à la place:

#include <iostream>
#include <list>

struct A {
    virtual ~A() {}   // important
};

struct B : A {};

void bar(A &a) { std::cout << "This is an A" << std::endl; }
void bar(B &b) { std::cout << "This is a B" << std::endl; }

void bar_helper(A *ptr)
{
    if ( auto b = dynamic_cast<B *>(ptr) )
        bar(*b);
    else
        bar(*ptr);
}

int main()
{
    std::list<A *> ls;
    ls.push_back(new B);
    ls.push_back(new B);
    ls.push_back(new A);

    for (auto ptr : ls)
    {
        bar_helper(ptr);
        delete ptr;
    }
    ls.clear();
}


0 commentaires

5
votes

Étant donné que la surcharge est résolue au moment de la compilation, vous devez fournir le compilateur avec suffisamment d'informations pour décider de la surcharge correcte de la barre code> à appeler. Depuis que vous souhaitez faire cette décision de manière dynamique sur le type de temps d'exécution de l'objet, les fonctions virtuelles seraient d'une grande aide:

std::list<A *> l;
l.push_back(new B());
l.push_back(new B());
for (std::list<A *>::iterator it = l.begin(); it != l.end(); ++it)
    (*it)->bar();


4 commentaires

Dasblinkenlight: tache sur.


@dasblinkenlight Yep, c'est ce que l'OP cherchait, je n'avais pas complètement compris. Hors de curiosité: Savez-vous que cela est considéré comme une méthode standard / recommandable de fonctionner?


@Antonio sous les contraintes que OP a décrit (c'est-à-dire aucun contrôle sur les surcharges existantes de la barre ) c'est raisonnablement courant. Par exemple, si on voulait fournir une couche de OOP sur la base de la bibliothèque C, il prendrait une approche similaire, mais au lieu de passer * ce il passerait une "poignée" obtenue à partir de la bibliothèque et stockée à l'intérieur d'une sous-classe.


J'avais essentiellement la même idée après avoir lu des commentaires. Pour ce que cela vaut la peine si c'est la meilleure réponse, ce qui sera ramassé et appelé intérieur B n'est pas si évident. Vous devez bien déclarer tout. Et l'exemple de travail a besoin de plus de travail. En particulier :: bar (a &) doit savoir a , puis A :: bar () doit savoir qu'il y a : : Barre (A &) Vous avez donc besoin de déclaration de terme pour que quelque chose puisse éviter les dépendances circulaires. Et vous devez vous rappeler, vous devez le déclarer pour chaque descendant de A qui doit le remplacer, sinon, le compilateur convertira implicitement B & à A & ...