Wywołanie metod klasy C++ za pomocą wskaźnika funkcji

Jak uzyskać wskaźnik funkcji dla funkcji członka klasy, a następnie wywołać tę funkcję z określonym obiektem? Chciałbym napisać:

class Dog : Animal
{
    Dog ();
    void bark ();
}

…
Dog* pDog = new Dog ();
BarkFunction pBark = &Dog::bark;
(*pBark) (pDog);
…

Również, jeśli to możliwe, chciałbym również wywołać konstruktor za pomocą wskaźnika:

NewAnimalFunction pNew = &Dog::Dog;
Animal* pAnimal = (*pNew)();    

Czy jest to możliwe, a jeśli tak, to jaki jest preferowany sposób, aby to zrobić?

Author: Jon Seigel, 2009-09-28

9 answers

Przeczytaj to dla szczegółów:

// 1 zdefiniuj wskaźnik funkcji i zainicjalizuj NA NULL

int (TMyClass::*pt2ConstMember)(float, char, char) const = NULL;

/ / C++

class TMyClass
{
public:
   int DoIt(float a, char b, char c){ cout << "TMyClass::DoIt"<< endl; return a+b+c;};
   int DoMore(float a, char b, char c) const
         { cout << "TMyClass::DoMore" << endl; return a-b+c; };

   /* more of TMyClass */
};
pt2ConstMember = &TMyClass::DoIt; // note: <pt2Member> may also legally point to &DoMore

// Calling Function using Function Pointer

(*this.*pt2ConstMember)(12, 'a', 'b');
 95
Author: Satbir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-22 19:39:39

Jak uzyskać wskaźnik funkcji dla funkcji członka klasy, a następnie wywołać tę funkcję z określonym obiektem?

Najłatwiej zacząć od typedef. Dla funkcji member, dodajesz nazwę klasy w deklaracji typu:

typedef void(Dog::*BarkFunction)(void);

Następnie do wywołania metody należy użyć operatora ->*:

(pDog->*pBark)();

Również, jeśli to możliwe, chciałbym wywołać konstruktor za pomocą wskaźnika. Czy jest to możliwe, a jeśli tak, to co jest preferowany sposób na to?

Nie wierzę, że można pracować z takimi konstruktorami - ctors i dtors są wyjątkowe. Normalnym sposobem osiągnięcia tego rodzaju rzeczy byłoby użycie metody fabrycznej, która jest w zasadzie tylko statyczną funkcją, która wywołuje konstruktor dla Ciebie. Zobacz poniższy kod dla przykładu.

Zmodyfikowałem Twój kod tak, aby robił to, co opisujesz. Poniżej są pewne zastrzeżenia.

#include <iostream>

class Animal
{
public:

    typedef Animal*(*NewAnimalFunction)(void);

    virtual void makeNoise()
    {
        std::cout << "M00f!" << std::endl;
    }
};

class Dog : public Animal
{
public:

    typedef void(Dog::*BarkFunction)(void);

    typedef Dog*(*NewDogFunction)(void);

    Dog () {}

    static Dog* newDog()
    {
        return new Dog;
    }

    virtual void makeNoise ()
    {
        std::cout << "Woof!" << std::endl;
    }
};

int main(int argc, char* argv[])
{
    // Call member function via method pointer
    Dog* pDog = new Dog ();
    Dog::BarkFunction pBark = &Dog::makeNoise;

    (pDog->*pBark)();

    // Construct instance via factory method
    Dog::NewDogFunction pNew = &Dog::newDog;

    Animal* pAnimal = (*pNew)();

    pAnimal->makeNoise();

    return 0;
}

Teraz chociaż można normalnie używać Dog* w miejsce Animal* dzięki magii polimorfizmu, typ wskaźnika funkcji Nie , a nie podąża za regułami hierarchii klas. Tak więc wskaźnik metody zwierzęcej nie jest kompatybilny ze wskaźnikiem metody psa, innymi słowy nie można przypisać Dog* (*)() do zmiennej typu Animal* (*)().

Metoda static newDog jest prostym przykładem fabryki, która po prostu tworzy i zwraca nowe instancje. Jest funkcją statyczną, ma regularną typedef (bez klasy Kwalifikacje).

Odpowiadając na powyższe, zastanawiam się, czy nie ma lepszego sposobu na osiągnięcie tego, czego potrzebujesz. Istnieje kilka konkretnych scenariuszy, w których można zrobić tego rodzaju rzeczy, ale może się okazać, że istnieją inne wzorce, które działają lepiej dla Twojego problemu. Jeśli opiszesz w bardziej ogólny sposób, co próbujesz osiągnąć, umysł ula może okazać się jeszcze bardziej przydatny!

Związane z powyższym, bez wątpienia znajdziesz Boost bind biblioteka i inne związane Moduły bardzo przydatne.

 50
Author: gavinb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-28 10:20:45

Myślę, że nikt nie wyjaśnił tutaj, że jeden problem jest to, że potrzebujesz "wskaźniki member " zamiast zwykłych wskaźników funkcji.

Wskaźniki członkowskie do funkcji nie są po prostu wskaźnikami funkcyjnymi. Jeśli chodzi o implementację, kompilator nie może użyć prostego adresu funkcji, ponieważ, ogólnie rzecz biorąc, nie znasz adresu do wywołania, dopóki nie wiesz, dla którego obiektu ma być odwołana (pomyśl o funkcjach wirtualnych). Musisz również znać obiekt, aby zapewnić this implicit parametr, oczywiście.

Powiedziawszy, że ich potrzebujesz, teraz powiem, że naprawdę musisz ich unikać. Poważnie, wskazówki dla członków to ból. O wiele bardziej rozsądne jest patrzenie na wzorce projektowe zorientowane obiektowo, które osiągają ten sam cel, lub użycie boost::function lub czegokolwiek, jak wspomniano powyżej-zakładając, że dokonasz tego wyboru, to znaczy.

Jeśli dostarczasz ten wskaźnik funkcji do istniejącego kodu, więc naprawdę potrzebujesz prostego wskaźnika funkcji, powinieneś napisać funkcja jako statyczny członek klasy. Statyczna funkcja członkowska nie rozumie this, więc musisz przekazać obiekt jako jawny parametr. Był kiedyś Niecodzienny idiom do pracy ze starym kodem C, który potrzebuje wskaźników funkcyjnych [11]}

class myclass
{
  public:
    virtual void myrealmethod () = 0;

    static void myfunction (myclass *p);
}

void myclass::myfunction (myclass *p)
{
  p->myrealmethod ();
}

Ponieważ myfunction jest tak naprawdę tylko normalną funkcją( pomijając kwestie zakresu), wskaźnik funkcji można znaleźć w normalnym C sposób.

EDIT - ten rodzaj metody nazywa się" metodą klasową " lub "static member function". Główną różnicą w stosunku do funkcji nieczłonkowej jest to, że jeśli odwołujesz się do niej spoza klasy, musisz określić zakres używając operatora rozdzielczości ::. Na przykład, aby uzyskać wskaźnik funkcji, Użyj &myclass::myfunction i wywołaj go użyj myclass::myfunction (arg);.

Tego typu rzeczy są dość powszechne przy użyciu starych API Win32, które pierwotnie zostały zaprojektowane dla C, a nie c++. Oczywiście w takim przypadku parametr jest zwykle LPARAM lub podobny, a nie wskaźnik, i potrzebny jest jakiś casting.

 27
Author: Steve314,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-07 11:41:53
typedef void (Dog::*memfun)();
memfun doSomething = &Dog::bark;
....
(pDog->*doSomething)(); // if pDog is a pointer
// (pDog.*doSomething)(); // if pDog is a reference
 17
Author: AraK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-28 11:34:33

Minimal runnable przykład

#include <cassert>

class C {
    public:
        int i;
        C(int i) : i(i) {}
        int m(int j) { return this->i + j; }
};

int main() {
    // Get a method pointer.
    int (C::*p)(int) = &C::m;

    // Create a test object.
    C c(1);
    C *cp = &c;

    // Operator .*
    assert((c.*p)(2) == 3);

    // Operator ->*
    assert((cp->*p)(2) == 3);
}

Nie można zmienić kolejności nawiasów ani ich pominąć. Nie działają:

c.*p(2)
c.*(p)(2)

C++11 standard

.* i ->* to operatory singe wprowadzone w C++ w tym celu, a nie obecne w C.

C++11 n3337 standard draft :

  • 2.13 "operatory i znaki interpunkcyjne" zawiera listę wszystkich operatorów, która zawiera .* i ->*.
  • 5.5 "Operatory Pointer-to-member" wyjaśniają, co robią
 7
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-05 08:47:48

Przyszedłem tutaj, aby dowiedzieć się, jak stworzyć wskaźnik funkcji (nie wskaźnik metody) z metody, ale żadna z odpowiedzi tutaj nie dostarcza rozwiązania. Więc pomyślałem o tym i znalazłem fajne rozwiązanie, które myślę, że warto się podzielić: {]}

template <class T> struct MethodHelper;
template <class C, class Ret, class... Args> struct MethodHelper<Ret(C::*)(Args...)> {
    using T = Ret (C::*)(Args...);
    template <T m> static Ret call(C* object, Args... args) {
        return (object->*m)(args...);
    }
};

#define METHOD_FP(m) MethodHelper<decltype(m)>::call<m>

Więc dla Twojego przykładu zrobiłbyś teraz:

Dog dog;
using BarkFunction = void (*)(Dog*);
BarkFunction bark = METHOD_FP(&Dog::bark);
(*bark)(&dog); // or simply bark(&dog)
 7
Author: eyelash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-03 10:11:24

Powodem, dla którego nie można używać wskaźników funkcji do wywoływania funkcji Członkowskich jest to, że zwykłe wskaźniki funkcji są zwykle tylko adresem pamięci funkcji.

Aby wywołać funkcję member, musisz wiedzieć dwie rzeczy:

  • jaką funkcję członkowską wywołać
  • która instancja powinna być użyta (czyja funkcja członkowska)

Zwykłe wskaźniki funkcji nie mogą przechowywać obu. C++ member function pointers are used aby zapisać a), dlatego należy określić instancja jawnie podczas wywoływania wskaźnika funkcji członka.

 6
Author: hrnt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-28 08:55:27

Wskaźnik funkcji do członka klasy jest problemem, który naprawdę nadaje się do używania metody boost::function. Mały przykład:

#include <boost/function.hpp>
#include <iostream>

class Dog 
{
public:
   Dog (int i) : tmp(i) {}
   void bark ()
   {
      std::cout << "woof: " << tmp << std::endl;
   }
private:
   int tmp;
};



int main()
{
   Dog* pDog1 = new Dog (1);
   Dog* pDog2 = new Dog (2);

   //BarkFunction pBark = &Dog::bark;
   boost::function<void (Dog*)> f1 = &Dog::bark;

   f1(pDog1);
   f1(pDog2);
}
 6
Author: Benjamin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-28 13:18:36

Aby utworzyć nowy obiekt, możesz użyć placement new, jak wspomniano powyżej, lub zlecić klasie implementację metody clone (), która tworzy kopię obiektu. Następnie można wywołać tę metodę klonowania za pomocą wskaźnika funkcji członka, jak wyjaśniono powyżej, aby utworzyć nowe instancje obiektu. Zaletą clone jest to, że czasami możesz pracować ze wskaźnikiem do klasy bazowej, w której nie znasz typu obiektu. W tym przypadku metoda clone() może być łatwiejsza w użyciu. Również klon() pozwoli Ci skopiować stan obiektu, jeśli tego chcesz.

 2
Author: Corwin Joy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-28 09:34:06