Bonjour à tous,
en C++, il est possible de transmettre une fonction par référence à une méthode (au sens de l'objet) ou a une autre fonction.
Mais est-il possible de transmettre une méthode (au sens de l'objet) par référence?
merci
Bonjour à tous,
en C++, il est possible de transmettre une fonction par référence à une méthode (au sens de l'objet) ou a une autre fonction.
Mais est-il possible de transmettre une méthode (au sens de l'objet) par référence?
merci
kamoulox
tu veux faire quoi ou tu crois que quoi n'est pas possible ?
méthode = fonction donc tes 2 phrases sont identiques, et "au sens de l'objet" c'est sensé vouloir dire quoi ?
Bonjour,
Je voudrais savoir si donner une méthode en argument d'une fonction est possible, comme il est possible de le faire pour une fonction.
"Au sens de l'objet" avait pour but de préciser qu'il s'agit bien d'une méthode implémentée au sein d'une classe. Cette précaution oratoire était peut-être inutile, mais mon objectif était de me faire comprendre au mieux.
Méthode != fonction. Une méthode est propre à une classe, ce que n'est pas une fonction. De plus, concernant le problème qui nous intéresse, j'ai vu plusieurs sources indiquant que pthread_create, par exemple, n'acceptait en argument que des méthodes statiques.
Ce que t'appelles méthode c'est une fonction membre, et choisir l'un ou l'autre des noms est une nomenclature que chacun suit ou non et absolument pas un standard.
La seule différence c'est leur signature.
Et passer une fonction en paramètre c'est créer un foncteur.
pthread_create c'est une lib en C où les fonctions membre n'existent pas. Ça prend ce que le C permet, des fonctions libres (ou static).
J'appelle méthode une méthode au sens de l'objet, et c'était très clair dans mon message.
Et il n'y a pas qu'une différence de signature entre une fonction et une méthode au sens de l'objet.
Il y a beaucoup plus simple pour passer une fonction en paramètre: lui transmettre un pointeur sur cette fonction, c'est ce qui est fait dans le code que j'ai récupéré, et je réfléchis à la réécrire en objet.
S'il n'y avait pas de différence entre méthode et fonction en C++, pthread_create accepterait des méthodes en argument.
Il existe std::thread pour ne pas avoir à ce préoccuper de toute la gestion des pointeurs de fonction. Sinon, un simple intermédiaire comme ce qui suit suffit:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 auto func = [](void* ctx) { static_cast<LeTypeVoulut*>(ctx).la_fonction(); return .... }; pthread_create(thread, attr, func, &obj);
En C++, on retrouve plutôt le terme de "fonction membre" (ou "member function", en anglais) que le terme de "méthode". Voir https://en.cppreference.com/w/cpp/la...mber_functions
Je suppose que ton problème est : comment passer un couple [objet ; fonction membre] là où tu passes habituellement un simple pointeur sur fonction, non ?
Pourquoi on parle de cast d'un seul coup ?
const_cast est possible en C :
Seul dynamic_cast est spécifique au C++.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 int main() { const int a = 2; int * p = (int*) &a; *p = 45; return a; }
Ok, je retiens ce point de vocabulaire propre au C++.
De mon coté, j'ai appris l'objet à la fois dans des ouvrages qui décrivent théoriquement le concept (et utilisent le terme méthode) et en python (avec un cours qui utilisait le terme méthode), ceci explique cela.
C'est ça, voilà.
Après cela pourrait aussi peut-être le faire avec ce que j'appelle une méthode de classe/méthode statique, à savoir une méthode de la classe qui n'est pas associée à une instance de cette classe (peut-être "fonction membre statique" en C++).
Si tu sais déjà tout pourquoi venir poser des questions alors ?
C'est un nouveau concept de venir avec des questions, dire qu'en fait non on a raison et on sait mieux les réponses et surtout ne jamais présenter de code incriminé ?
On utilisait pthread y'a 20 ans déjà, tout comme le C++, et les deux ensembles. Des ressources qui montrent comment faire sont légion sur internet pour qui veut bien chercher.
Sur ce seul forum il y a des dizaines de résultats à ce sujet. Avec le simple "pthread_create", donc pas vraiment un terme caché ou obscur, pour faire ressortir des résultats probants.
https://www.developpez.net/forums/d1.../#post10432689
https://www.developpez.net/forums/d1...thread_create/
https://www.developpez.net/forums/d1...e/#post8106791
https://cpp.developpez.com/faq/cpp/?...ur-de-fonction
https://www.developpez.net/forums/d1...s/#post7553057
https://www.developpez.net/forums/d1...e/#post7552025
https://www.developpez.net/forums/d1...ction-d-objet/
Et maintenant on préfèrera le plus souvent std::thread qui est dans le standard depuis C++11.
Merci pour ton intervention.
Je rajoute cependant que mon objectif n'est pas d'utiliser pthread_create, j'utilise std::thread effectivement. J'ai cité cet exemple pour illustrer une différence entre méthode et fonction (au sens que je donne à ces termes, mais il n'y a pas que moi quand même ).
Moi j'ai simplement:
avec fct est est une fonction, et je voudrais savoir si je peux remplacer cette fonction par une fonction membre.
Code : Sélectionner tout - Visualiser dans une fenêtre à part obj.fctMembre(&fct, &err);
voilà voilà
A ta décharge, je crois que tout le monde dit "méthode", sauf le C++
Si tu veux vraiment un pointeur sur fonction, alors ce ne sera pas possible. Il te faudra une fonction libre, une fonction membre statique, ou une lambda non capturante.
Mais si tu utilises std::thread, tu n'es pas limité à un pointeur sur fonction. En regardant les constructeurs, on voit que la signature (sans doute) la plus utilisée est la 3e :
En dessous, on lit :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 template< class Function, class... Args > explicit thread( Function&& f, Args&&... args );
Un Callable est plus beaucoup général qu'un pointeur sur fonction.
Code : Sélectionner tout - Visualiser dans une fenêtre à part f - Callable object to execute in the new thread
Tu peux utiliser une lambda capturante (j'ai écrit un article plutôt complet sur les lambdas) :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 #include <iostream> #include <thread> struct Foo { void f() const { std::cout << "action!\n"; } }; int main() { Foo foo; auto lambda = [foo]() { foo.f(); }; std::thread thread{lambda}; thread.join(); }
Tu peux utiliser un functor ou function objet :
Je pense que ça devrait t'aider
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 #include <iostream> #include <thread> struct Foo { void operator()() const { std::cout << "action!\n"; } }; int main() { Foo foo; std::thread thread{foo}; thread.join(); }
Salut,
Le gros problème, avec les "fonctions membres" -- ou les "méthodes"(*) -- c'est que le compilateur va automatiquement faire "quelque chose" pour permettre à la fonction de savoir à partir de quelle instance de la classe la fonction a été appelée.
En effet, une fonction "libre" ne va nécessiter que les paramètres dont la liste est fournie. Ainsi, si tu as une fonction libre qui prend la forme de
il n'y a -- vraiment -- que trois paramètres qui sont requis pour pouvoir faire appel à cette fonction.
Code : Sélectionner tout - Visualiser dans une fenêtre à part void foo(int, float)
Par contre, si on envisage une fonction équivalente qui serait une fonction membre d'une classe quelconque, par exempleNous avons affaire à une fonction dont l'exécution dépend expressément d'une instance bien particulière de MaClasse.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 class MaClasse{ public: void bar(int, float); // le nom est différent, mais la signature est "sensiblement" la même n'est-ce pas? /* ... */ };
En effet, on peut "décemment" estimer que la classe MaClasse va également contenir "un certain nombre" de données "internes", qui seront susceptibles de varier d'une instance à l'autre de cette classe. Il faut donc pouvoir ... récupérer au niveau de la fonction les données qui dépendent de l'instance particulière de MaClasse à partir de laquelle la fonction a été appelée.
Or, une fois que le code a été traduit en langage machine, il n'y a absolument aucune différence dans la manière dont sont traitées les fonctions libres et les fonctions membres.
Par exemple, quand bien même nous aurions des dizaines, voire des centaines d'instances de MaClasse, la fonction bar n'existerait qu'en un seul et unique exemplaire ( du moins, si elle n'est pas déclarée inline) et serait appelée exactement de la même manière que la fonction foo, à savoir:
- on garde l'adresse mémoire de l'instruction qui suit l'appel "en mémoire"
- on prend les paramètres transmis lors de l'appel de la fonction en charge
- on saute à l'adresse mémoire à laquelle les instructions spécifiques à la fonction se trouvent
- une fois l'exécution de la fonction terminée, on reprend l'adresse de l'instruction qui suit l'appel pour continuer le travail
(bon, c'est simplifié, mais le principe est bel et bien correct )
La grosse différence qu'il va donc y avoir entre une fonction libre et une fonction membre va donc tenir dans le faite que le compilateur va automatiquement ajouter un paramètre -- un pointeur sur MaClasse qu'il appellera this -- comme premier paramètre de la fonction membre, et ce, sans même nous demander notre avis.
La fonction membre pourrait donc parfaitement être représentée sous la forme de
et, peut-être, commences tu dés lors à comprendre où le problème risque de se situer
Code : Sélectionner tout - Visualiser dans une fenêtre à part void bar(MaClasse * this, int, float)
BINGO! Le fait est que, si j'ai deux classes différentes qui exposent toutes les deux une fonction portant le même nom, nécessitant les mêmes paramètres, et renvoyant le même type de valeur (ou ne renvoyant rien, dans le cas présent) sous la forme de
Hé bien, la seule chose qui va changer entre les deux fonctions, ce sera ... le type de la donnée pointée par this: Ce sera une donnée de type MaClasse pour la première, et une donnée de type AutreClasse pour la deuxième.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 class MaClasse{ public: void bar(int, float); /* ... */ }; class AutreClasse{ public: void bar(int, float); /* ... */ }; /* et l'utilisation */ int main(){ MaClasse c; c.bar(10, 3.1415926); AutreClasse a; a.bar(10, 3.1415926); }
Et il va de soi qu'il est totalement impensable de se retrouver dans une situation dans laquelle ce serait la "version" de bar issue de MaClasse qui serait appelée alors que nous avons fait appel à bar à partir d'une instance de AutreClasse
C'est donc là que le gros problème va apparaitre: Pour pouvoir transmettre la fonction membre d'une classe à une autre fonction, il faut impérativement également transmettre ... un pointeur sur l'instance à partir de laquelle la fonction devra être appelée.
Ou bien il faut que la fonction qui sera appelée ne dépende d'aucune instance particulière de la classe (il faut donc que ce soit une fonction membre statique).
Enfin, ** l'idéal ** serait sans doute d'utiliser la classe std::function pour créer tes callbacks, car cette classe offre pas mal de possibilités
Nous pourrions par exemple envisager d'avoir un code (complet) qui prendrait la forme de
ou, si les fonctions auxquelles tu souhaite faire appel ne dépendent d'aucune instance particulière de la classe à partir de laquelle elles sont appelée, tu pourrais aussi envisager un code proche de
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 #include <iostream> #include <functional> class MaClasse{ public: MaClasse(int id):id{id}{ } void bar(int i, float f) const{ std::cout<<"bar called from "<<id <<" with parameters \n" <<i<<" as integer\n" <<"and "<<f<<" as float\n"; } void truc(int i, float f) const{ std::cout<<"truc called from "<<id <<" with parameters \n" <<i<<" as integer\n" <<"and "<<f<<" as float\n"; } private: int id; }; using callback = std::function<void(MaClasse*, int, float)>; void foo(MaClasse * ptr, callback c, int i, float f){ c(ptr, i, f); } int main(){ MaClasse c{3}; foo(&c,MaClasse::bar,10, 3.141592); foo(&c,MaClasse::truc,10, 3.141592); return 0; }(note que, dans les deux cas, j'ai préféré utiliser un alias de type pour le callback, je trouve cela plus "facile" )
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 #include <iostream> #include <functional> class MathComputations{ public: static int add(int a, int b){ return a + b; } static int multiply(int a, int b){ return a * b; } }; using callback = std::function<int ( int, int )>; int foo( callback c, int a, int b){ return c(a, b); } int main(){ std::cout<<" 3 + 4 = "<<foo(MathComputations::add, 3,4); std::cout<<"\n"; std::cout<<" 3 * 4 = "<<foo(MathComputations::multiply, 3,4); return 0; }
(*)Le fait est que le terme "méthode" pêche quelque part (en tout cas en C++) par sa spécificité alors qu'il est présenté dans un contexte dans lequel il est ** censé ** être le plus générique possible Mais, laisse moi t'expliquer cette phrase
Dans la plupart des langages orientés objets, une fonction qui appartient à une classe n'a que deux possibilités:
- Soit, c'est une fonction qui ne dépend d'aucune instance particulière de la classe dans laquelle elle se trouve ( disons "fonction statique", par facilité)
- Soit, c'est une fonction dont le comportement peut automatiquement être redéfini dans les classes dérivées (nous pourrions la qualifier de "virtuelle")
Par exemple, en java, tu pourrais trouver une hiérarchie de classes proche de
Et cette possibilité de redéfinir le comportement dans la classe dérivée est systématique pour l'ensemble de toutes les fonctions (non statiques) exposées par la classe de base.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 public class Base{ public void afficher(){ system.printnl("ceci est une base"); } }; public class Derived inherits Base{ public void afficher(){ system.printnl("ceci est une derivee"); } }
Ces deux catégories (fonctions "statiques" Vs fonction "virutelles") suffisent amplement pour la plupart des langages, car si la fonction dépend d'une instance bien particulière de la classe, on s'attend effectivement à ce que son comportement soit susceptible d'être adapté en fonction du type réel de l'instance à partir de laquelle la fonction sera appelée (c'est ce que l'on appelle le polymorphisme d'inclusion)
Seulement, voilà, les choses sont "un peu plus complexes" en C++. Car, par défaut, le comportement d'une fonction membre n'est pas susceptible d'être redéfini dans les classes. Pour que le comportement d'une fonction membre soit susceptible d'être redéfini dans la classe dérivée, il faut impérativement déclarer cette fonction comme virtuelle au niveau de la classe de base (et ** idéalement ** déclarer son comportement comme "redéfini" dans les classes dérivées qui redéfinissent le comportement).
Nous avons donc trois possibilités distinctes en C++:
- les fonctions membres "statiques", indépendante de toute instance particulière de la classe
- les fonctions membres "non statiques" et "non virtuelles", dont le comportement n'est pas susceptible d'être redéfini dans les classes dérivées
- les fonctions membres "non statiques" et "virtuelles", dont le comportement est susceptible d'être redéfini dans les classes dérivées.
Mais, du coup, même si l'on ne tient pas compte des fonctions membres statiques (dont l'exécution ne dépend d'aucune instance particulière de la classe) la notion de "méthode" telle qu'elle est présentée de manière générale dans les ouvrages destinés à la conception ne correspond qu'à une partie seulement des possibilités qui sont offertes par le langage.
Pire encore, cette notion correspond en définitive à l'exception du langage, vu que ce que permet cette notion n'a absolument rien "d'automatique", et doit au contraire être demandé explicitement.
C'est la raison pour laquelle nous parlerons beaucoup plus souvent de "fonction membre" (statique ou non statique, éventuellement virtuelle) qye de méthode en C++
Messieurs (Dames, qui sait),
merci à vous tous pour vos interventions que j'ai lues attentivement. Je rajoute la réponse qui m'a semblé la plus simple à ma question (question fort basique, au demeurant).
Initialement j'avais quelque-chose comme:
Qui peut se réécrire en objet:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 void test(int a) { cout << "entier :" << a << endl; } void function1(void (*function)(int)) { function(1); } int main() { function1(&test); }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 class aClass { public: void aTest(int a) { cout << "entier :" << a << endl; } }; void function2(void (aClass::*function)(int), aClass& a) { (a.*function)(1); } int main() { aClass a; function2(&aClass::aTest, a); }
Ben... non... Une méthode/fonction membre peut être "private" en java, et aucune autre classe ne pourra en hériter. Ou alors je n'ai pas bien compris.Dans la plupart des langages orientés objets, une fonction qui appartient à une classe n'a que deux possibilités:
Soit, c'est une fonction qui ne dépend d'aucune instance particulière de la classe dans laquelle elle se trouve ( disons "fonction statique", par facilité)
Soit, c'est une fonction dont le comportement peut automatiquement être redéfini dans les classes dérivées (nous pourrions la qualifier de "virtuelle")
De plus, le terme de méthodes est bien parfois utilisé en C++. J'ai bien compris que ce n'était pas l'usage le plus courant, mais ce n'est pas inexistant non plus:
Tutorial C++ : Définition des méthodes
http://william.arrouy.free.fr/cpp/cpp3.htmlUne classe va permettre de regrouper en une seule entité des données membres et des fonctions membres appelées méthodes.
https://www.fresnel.fr/perso/stout/l...es_Classes.pdfdéclarations de méthodes;
http://igm.univ-mlv.fr/~lombardy/ens...1/cpp2poly.pdfLes traitements attachés à une classe sont appelés méthodes (ou fonctions membres, opérations).
https://www.labri.fr/perso/bourqui/d...2B-classes.pdf
Mais on s'en fout en fait de l'accessibilité!
Car tout -- une donnée, un fonction ou même un type de donnée, s'il est imbriqué dans une autre classe-- peut être public ou privé.
L'accessibilité n'est qu'une convention que l'on met en place entre l'homme et le compilateur (ou l'interpréteur) pour signaler que l'accès à "quelque chose" est "plus ou moins limité", parce que l'on peut faire confiance au compilateur (ou à l'interpréteur) pour être "plus buté" que nous, et que, si on lui dit que "on ne peut accéder à telle chose qu'à partir des fonctions qui font partie de a classe", on peut être sur qu'il nous engueulera si on essaye d'accéder à la chose en question à partir de "l'extérieur" de la classe.
Mais, une fois le code binaire exécutable généré pour que le processeur puisse le prendre en charge, la notion d'accessibilité n'est plus représentée nulle part, et une fonction, une donnée, ou même un type de donnée ne seront pas représentés différemment dans le code binaire exécutable s'ils ont été marqués privés que s'ils ont été définis comme publics.
Par contre, ce qui va effectivement changer pas mal de choses, c'est de savoir si "quelque chose" -- qu'il s'agisse encore une fois d'une donnée, d'une fonction ou même d'un type de donnée -- qui est fourni comme "faisant partie d'une classe" dépend effectivement d'une instance bien particulière de la classe ou non.
Parce que, si "quelque chose" dépend d'une instance particulière de la classe, ben, nous n'aurons pas d'autre choix que de fournir l'instance dont il dépend pour pouvoir y accéder alors que, si ce "quelque chose" ne dépend d'aucune instance particulière, ben, on peut y accéder sans même avoir pris la peine de créer la moindre instance de la classe.
Attention, je ne dis pas que la notion d'accessibilité est inutile. Je dis juste qu'elle n'est pas indispensable. Je dis juste que l'accessibilité et le principe sous-jacent à cette notion qu'est l'encapsulation
(*)En cherchant bien, je devrais pouvoir te retrouver l'une ou l'autre de mes interventions qui expliquent ce point. Je ne vais juste pas alourdir ma réponse aujourd'hui, tout en restant à ta disposition si tu veux des explications supplémentaires
- n'est pas le principe essentiel de la programmation orientée objet, car le vrai principe fondateur de cette approche est la substituabilité au sens du principe de substitution de Liskov (LSP)
- est souvent mal comprise, car les gens finissent trop souvent persuadé qu'il "suffit" de placer une donnée dans l'accessibilité privée et d'ajouter un accesseur (getter), ce qui fait parfois sens, et un mutateur (setter) pour que la donnée soit correctement encapsulée, ce qui est totalement faux (*)
- est parfaitement possible et mise en oeuvre y compris dans les langages purement impératifs comme le C. Qu'il te suffise de penser à la structure FILE, à laquelle on n'a accès qu'au travers d'un nombre réduit et clairement défini de fonctions qui effectuent des tâches clairement définies
Ouf, enfin un semblant de code dis donc...
Tout ceci est faisable avec std::function, ou une méthode template qui prend un callable et une lambda.
C'est beaucoup plus simple, clair et flexible et ça permet de l'utiliser avec ce qu'on veut.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 void fctMembre(std::function<void(int)> fct) { fct(1); } template<class Fct> void fctMembre(Fct fct) { fct(1); }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 class aClass { public: void aTest(int a) { cout << "entier :" << a << endl; } }; void function2(std::function<void(int)> fct) { fct(1); } template<class Fct> void function2(Fct fct) { fct(1); } void whatever(int i) {std::cout << i << std::endl; } int main() { aClass a; function2([&a](int i) { a.aTest(i); }); function3([&a](int i) { a.aTest(i); }); function2(whatever); }
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager