Вызов методов класса 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 ответов


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

/ / 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 путь.

редактировать - этот вид метода называется "методом класса"или" статической функцией-членом". Основное отличие от функции, не являющейся членом, заключается в том, что если вы ссылаетесь на нее извне класса, вы должны указать область, используя :: оператор разрешения области действия. Например, чтобы получить указатель функции, используйте &myclass::myfunction и назвать его use 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

минимальный runnable пример

#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 () позволит вам скопировать состояние объекта, если это то, что вы хотите.