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

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

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

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

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

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

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

95

Решение

Читать этот для деталей:

// 1 define a function pointer and initialize to 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');
101

Другие решения

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

Проще всего начать с 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* (*)(),

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

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

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

51

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

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

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

28

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

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

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)
8

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

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

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

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

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

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

6

Указатель на функцию в классе — это проблема, которая действительно подходит для использования 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);
}
6
По вопросам рекламы [email protected]