2017-01-05 2 views
3

Il y a quelques années, avant de connaître C++, je parlais avec un collègue programmeur expérimenté de la création de systèmes d'événements, etc. Il semblait mettre l'accent sur la nécessité de passer des pointeurs de fonction pour que les rappels puissent Cependant, ces jours où j'ai besoin d'implémenter un système d'événements, je construis simplement une pseudo-classe d'interface, j'en hérite et j'utilise le modèle abonné/observateur pour remplacer les méthodes. et distribuer des événements.C++, pointeurs de fonctions vs modèle d'observateur

Je pensais l'autre jour que jusqu'à présent, j'ai rarement besoin d'utiliser des pointeurs de fonction, certainement pas pour le genre de situation ci-dessus. La seule et unique situation où j'ai vraiment dû utiliser des pointeurs de fonction était lors de l'interfaçage avec le fichier .dll de quelqu'un d'autre qui en demandait explicitement un.

Quand devrais-je utiliser des pointeurs de fonction au lieu du modèle abonné/observateur?

Est-ce bon ou mauvais d'utiliser l'un ou l'autre? (il est probable qu'il doit y avoir des cas où le modèle d'observateur n'est pas meilleur qu'un pointeur de fonction).

S'il vous plaît quelqu'un pourrait partager un aperçu? Ce serait bien d'avoir plus d'expérience à ce sujet? (Le collègue susmentionné a déménagé dans une autre entreprise et nous avons perdu le contact). Je continue d'ébranler mon cerveau sur ce sujet et ne peux pas penser à quand il ne serait pas plus facile d'utiliser le modèle d'observateur et une interface.

Merci!

Exemple d'une classe je me retrouve souvent écrit (écrit sans essai, complètement hors du haut de ma tête, pourrait ne pas en fait):

class NotifierListener { 

friend class Notifier; 
private: 
vector<NotifierListener*> mListeners; 

protected: 
void subscribe(NotifierListener* _subscriber) {mListeners->push_back(_subscriber);} 
virtual void onNotification(); 
} 

class Notifier : public NotifierListener { 

private: 
void doSomethingAndNotify() { 

... 
if (mListeners.size() > 0) { 
for (int i = 0; i < mListeners.size(); ++i) { 

mListeners[i]->onNotification(); 
} 
} 

} 
} 

class Something : public NotifierListener { 

Something() { subscribe(this); } 

void onNotification() { 

cout << "I got notified\n" << endl; 
} 
} 
+2

Les rappels via les pointeurs de fonction constituent également une implémentation possible du modèle d'observateur. Vous devriez illustrer votre question avec du code montrant vos implémentations. – wasthishelpful

+0

Je suis d'accord qu'une illustration de code ajoute de la valeur et de la clarté. Mais mon problème ici est que plutôt que de vouloir un exemple de mise en œuvre de code spécifique, je m'intéresse davantage à une explication générale du «quand» et non du «comment». Je ne peux vraiment pas penser à un code à écrire qui ne serait pas clairement et évidemment mieux écrit en tant que méthode virtuelle. –

Répondre

2

Les pointeurs de fonction sont un peu plus efficaces. Vous passez maintenant un pointeur sur une interface. L'interface généralement * contient un pointeur vers un vtable. Et cette vtable contient à son tour des pointeurs de fonction. C'est 3 niveaux d'indirection, avec une mauvaise localisation de référence.

[*] D'autres implémentations sont possibles, mais ont une charge similaire.

2

Comme l'a souligné wasthishelpful, l'héritage et Les pointeurs de fonction sont deux façons d'implémenter le modèle d'observateur.

L'avantage principal de l'utilisation de pointeurs de fonction (ou plus précisément de quelque chose comme std::function de C++ 11) est une flexibilité accrue. La classe observante n'a pas besoin d'implémenter une interface et la fonction membre appelée peut être arbitrairement nommée. Vous pouvez même appeler une fonction autonome ou un membre d'un objet que vous ne pouvez pas ou ne voulez pas modifier pour implémenter votre interface d'observateur. Par contre, l'utilisation de l'héritage est plus simple et plus directe. Mais à part ça, je ne peux pas penser à un avantage par rapport à l'approche du pointeur de fonction.

1

En Observer Pattern, les écouteurs sont des entités qui vont prendre certaines mesures lors de la réception d'événements. La plupart du temps, ces entités font leur travail individuel et écoutent les événements et invoquent des actions éventuellement différentes en fonction de l'état dans lequel elles se trouvent lorsqu'elles reçoivent les événements. Donc, un meilleur choix est que les objets concrets soient les auditeurs, puisque les objets ont un état individuel. Tout en maintenant de tels états de manière cohésive/transparente en utilisant la fonction pure (qui sera invoquée en utilisant des pointeurs de fonction) est un peu difficile.