Вызов методов класса C++ через указатель на функцию


Как получить указатель на функцию для функции-члена класса, а затем вызвать эту функцию-член с определенным объектом? Я хотел бы написать:

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

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

также, если возможно, я хотел бы вызвать конструктор с помощью указателя:

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

возможно ли это, и если да, то каков предпочтительный способ сделать это?

9 89

9 ответов:

читать этой детали :

/ / 1 Определите указатель на функцию и инициализируйте значение 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');

как получить указатель на функцию для функции-члена класса, а затем вызвать эту функцию-член с определенным объектом?

проще всего начать с typedef. Для функции-члена добавьте имя класса в объявление типа:

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

затем, чтобы вызвать метод, вы используете ->* оператор:

(pDog->*pBark)();

также, если это возможно, я хотел бы вызвать конструктор с помощью указателя. Есть это возможно, и если да, то каков предпочтительный способ сделать это?

я не верю, что вы можете работать с конструкторами, как это - ctors и dtors являются особенными. Обычным способом достижения такого рода вещей было бы использование метода фабрики, который в основном является просто статической функцией, которая вызывает конструктор для вас. См. код ниже для примера.

я изменил ваш код, чтобы сделать в основном то, что вы описали. Есть некоторые предостережения под.

#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;
}

теперь, хотя вы обычно можете использовать Dog* вместо Animal* благодаря магии полиморфизма, тип указателя функции делает не следуйте правилам поиска иерархии классов. Таким образом, указатель метода Animal не совместим с указателем метода Dog, другими словами, вы не можете назначить Dog* (*)() переменной типа Animal* (*)().

статический!--9--> метод является простым примером фабрики, которая просто создает и возвращает новые экземпляры. Будучи статической функцией, она имеет регулярное typedef (без класса квалификации).

ответив на вышеизложенное, я задаюсь вопросом, нет ли лучшего способа достижения того, что вам нужно. Есть несколько конкретных сценариев, где вы могли бы сделать такие вещи, но вы можете обнаружить, что есть и другие шаблоны, которые лучше работают для вашей проблемы. Если вы опишете в более общих терминах то, что вы пытаетесь достичь, коллективный разум может оказаться еще более полезно!

связанные с вышеизложенным, вы, несомненно, найдете Boost bind библиотека и другие связанные с ней модули очень полезны.

Я не думаю, что кто-то объяснил здесь, что одна проблема заключается в том, что вам нужно "указатели-члены" вместо обычных указателей на функции.

указатели на функции-это не просто указатели на функции. В терминах реализации компилятор не может использовать простой адрес функции, потому что, как правило, вы не знаете адрес для вызова, пока не узнаете, какой объект для разыменования (подумайте о виртуальных функциях). Вы также должны знать объект, чтобы обеспечить this неявный параметр, конечно.

сказав, что они вам нужны, теперь я скажу, что вам действительно нужно их избегать. Серьезно, указатели членов-это боль. Гораздо более разумно смотреть на объектно-ориентированные шаблоны проектирования, которые достигают той же цели, или использовать boost::function или что - то еще, как упоминалось выше-предполагая, что вы сделаете этот выбор, то есть.

если вы предоставляете этот указатель функции на существующий код, так что вы действительно нужно простой указатель на функцию, вы должны написать функцию как статический член класса. Статическая функция-член не понимает this, Так что вам нужно передать объект в качестве явного параметра. Когда-то была не такая уж необычная идиома в этих строках для работы со старым кодом C, который нуждается в указателях функций

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

    static void myfunction (myclass *p);
}

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

С myfunction действительно просто нормальная функция (проблемы с областью действия в стороне), указатель функции можно найти в обычном C путь.

EDIT - этот вид метода называется "метод класса"или" статическая функция-член". Основное отличие от функции, не являющейся членом, заключается в том, что если вы ссылаетесь на нее из-за пределов класса, вы должны указать область с помощью :: оператор разрешения области действия. Например, чтобы получить указатель на функцию, используйте &myclass::myfunction и назвать его использовать myclass::myfunction (arg);.

такого рода вещи довольно распространены при использовании старых Win32 API, которые были первоначально разработаны на C вместо C++. Конечно, в этом случае параметр обычно является LPARAM или подобным, а не указателем, и требуется некоторое приведение.

typedef void (Dog::*memfun)();
memfun doSomething = &Dog::bark;
....
(pDog->*doSomething)(); // if pDog is a pointer
// (pDog.*doSomething)(); // if pDog is a reference

минимальный работоспособный пример

#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);
}

вы не можете изменить порядок скобках или без них. Следующие не работают:

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

стандарт C++11

.* и ->* - это операторы подпалить введено в C++ для этой цели, а не присутствует в C.

в C++11 N3337 типовой проект:

  • 2.13 "операторы и знаки пунктуации" имеет список все операторы, которые содержат .* и ->*.
  • 5.5 "операторы указателя на член" объясняет, что они делают

Я пришел сюда, чтобы узнать, как создать указатель функции (не указатель метода) из метода, но ни один из ответов здесь не дает решения. Поэтому я подумал об этом и нашел хорошее решение, которое, я думаю, стоит поделиться:

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>

Так что для вашего примера вы теперь делать:

Dog dog;
using BarkFunction = void (*)(Dog*);
BarkFunction bark = METHOD_FP(&Dog::bark);
(*bark)(&dog); // or simply bark(&dog)

причина, почему вы не можете использовать указатели на функции для вызова функций-членов, что обычные указатели на функции, как правило, просто адрес памяти функции.

чтобы вызвать функцию-член, вам нужно знать две вещи:

  • какую функцию-член вызвать
  • какой экземпляр следует использовать (чья функция-член)

обычные указатели на функции могут хранить. Используются указатели на функции-члены C++ для хранения), которая почему необходимо явно указать экземпляр при вызове указателя функции-члена.

указатель функции на член класса-это проблема, которая действительно подходит для использования boost::function. Небольшой пример:

#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);
}

чтобы создать новый объект, вы можете либо использовать placement new, как указано выше, либо реализовать в своем классе метод clone (), который создает копию объекта. Затем можно вызвать этот метод клонирования с помощью указателя функции-члена, как описано выше, чтобы создать новые экземпляры объекта. Преимущество клона заключается в том, что иногда вы можете работать с указателем на базовый класс, где вы не знаете тип объекта. В этом случае метод clone() может быть проще в использовании. Также, clone () позволит вам скопировать состояние объекта, если это то, что вы хотите.