Вызов методов класса С++ с помощью указателя функции

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

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

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

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

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

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

Ответ 1

Подробнее прочитайте этот:

//1 определить указатель на функцию и инициализировать значение NULL

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

//С++

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');

Ответ 2

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

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

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

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

(pDog->*pBark)();

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

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

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

#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* (*)().

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

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

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

Ответ 3

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

Элементы-указатели на функции - это не просто указатели на функции. В условиях реализации компилятор не может использовать простой адрес функции, поскольку, как правило, вы не знаете адрес для вызова, пока не узнаете, к какому объекту относится к разыменованию (думать о виртуальных функциях). Вы также должны знать объект, чтобы обеспечить 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); ,

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

Ответ 4

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

Ответ 5

Минимальный допустимый пример

#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 "Операторы" указатель-член "объясняют, что они делают

Ответ 6

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

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)

Ответ 7

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

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

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

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

Ответ 8

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

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

Ответ 9

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

Ответ 10

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