如何创建指向成员函数的指针并调用它?

发布于 2024-08-06 13:26:34 字数 364 浏览 5 评论 0原文

如何获取类成员函数的函数指针,然后使用特定对象调用该成员函数?我想写:

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

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

另外,如果可能的话,我也想通过指针调用构造函数:

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

这可能吗?如果可以,首选的方法是什么?

How do I obtain a function pointer for a class member function, and later call that member function with a specific object? I’d like to write:

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

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

Also, if possible, I’d like to invoke the constructor via a pointer as well:

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

Is this possible, and if so, what is the preferred way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

场罚期间 2024-08-13 13:26:34

阅读了解详细信息:

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

Read this for detail :

// 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');
蓝颜夕 2024-08-13 13:26:34

如何获取类成员函数的函数指针,然后使用特定对象调用该成员函数?

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*,但函数指针的类型却不能 em> 遵循类层次结构的查找规则。因此 Animal 方法指针与 Dog 方法指针不兼容,换句话说,您不能将 Dog* (*)() 分配给 Animal* (*) 类型的变量()

静态 newDog 方法是工厂的一个简单示例,它只是创建并返回新实例。作为一个静态函数,它有一个常规的 typedef (没有类限定符)。

回答完上述问题后,我确实想知道是否没有更好的方法来实现您的需求。在一些特定的场景中,您会执行此类操作,但您可能会发现还有其他模式更适合您的问题。如果你用更笼统的术语描述你想要实现的目标,蜂巢思维可能会更有用!

与上述相关,您无疑会找到 Boost bind 库和其他相关模块非常有用。

How do I obtain a function pointer for a class member function, and later call that member function with a specific object?

It's easiest to start with a typedef. For a member function, you add the classname in the type declaration:

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

Then to invoke the method, you use the ->* operator:

(pDog->*pBark)();

Also, if possible, I’d like to invoke the constructor via a pointer as well. Is this possible, and if so, what is the preferred way to do this?

I don't believe you can work with constructors like this - ctors and dtors are special. The normal way to achieve that sort of thing would be using a factory method, which is basically just a static function that calls the constructor for you. See the code below for an example.

I have modified your code to do basically what you describe. There's some caveats below.

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

Now although you can normally use a Dog* in the place of an Animal* thanks to the magic of polymorphism, the type of a function pointer does not follow the lookup rules of class hierarchy. So an Animal method pointer is not compatible with a Dog method pointer, in other words you can't assign a Dog* (*)() to a variable of type Animal* (*)().

The static newDog method is a simple example of a factory, which simply creates and returns new instances. Being a static function, it has a regular typedef (with no class qualifier).

Having answered the above, I do wonder if there's not a better way of achieving what you need. There's a few specific scenarios where you would do this sort of thing, but you might find there's other patterns that work better for your problem. If you describe in more general terms what you are trying to achieve, the hive-mind may prove even more useful!

Related to the above, you will no doubt find the Boost bind library and other related modules very useful.

烂柯人 2024-08-13 13:26:34

我认为没有人在这里解释过一个问题是你需要“成员指针" 而不是普通的函数指针。

指向函数的成员指针不仅仅是函数指针。在实现方面,编译器不能使用简单的函数地址,因为通常,在知道要取消引用哪个对象之前,您不知道要调用的地址(想想虚拟函数)。当然,您还需要知道该对象才能提供 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 时,这种情况相当常见,这些 API 最初是为 C 而不是 C++ 设计的。当然,在这种情况下,参数通常是 LPARAM 或类似的而不是指针,并且需要进行一些转换。

I don't think anyone has explained here that one issue is that you need "member pointers" rather than normal function pointers.

Member pointers to functions are not simply function pointers. In implementation terms, the compiler cannot use a simple function address because, in general, you don't know the address to call until you know which object to dereference for (think virtual functions). You also need to know the object in order to provide the this implicit parameter, of course.

Having said that you need them, now I'll say that you really need to avoid them. Seriously, member pointers are a pain. It is much more sane to look at object-oriented design patterns that achieve the same goal, or to use a boost::function or whatever as mentioned above - assuming you get to make that choice, that is.

If you are supplying that function pointer to existing code, so you really need a simple function pointer, you should write a function as a static member of the class. A static member function doesn't understand this, so you'll need to pass the object in as an explicit parameter. There was once a not-that-unusual idiom along these lines for working with old C code that needs function pointers

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

    static void myfunction (myclass *p);
}

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

Since myfunction is really just a normal function (scope issues aside), a function pointer can be found in the normal C way.

EDIT - this kind of method is called a "class method" or a "static member function". The main difference from a non-member function is that, if you reference it from outside the class, you must specify the scope using the :: scope resolution operator. For example, to get the function pointer, use &myclass::myfunction and to call it use myclass::myfunction (arg);.

This kind of thing is fairly common when using the old Win32 APIs, which were originally designed for C rather than C++. Of course in that case, the parameter is normally LPARAM or similar rather than a pointer, and some casting is needed.

软的没边 2024-08-13 13:26:34

最小可运行示例

main.cpp

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

编译并运行:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

在 Ubuntu 18.04 中测试。

您不能更改括号的顺序或省略它们。以下内容不起作用:

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

GCC 9.2 将失败:

main.cpp: In function ‘int main()’:
main.cpp:19:18: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘p (...)’, e.g. ‘(... ->* p) (...)’
   19 |     assert(c.*p(2) == 3);
      |

C++11 标准

.*->* are a 为此目的在 C++ 中引入了单个运算符,但在 C 中不存在。

C++11 N3337 标准草案

  • 2.13“运算符和标点符号”有所有运算符的列表,其中包含 .*-> ;*
  • 5.5 “指向成员的指针运算符”解释了它们的作用

Minimal runnable example

main.cpp

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

Compile and run:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

Tested in Ubuntu 18.04.

You cannot change the order of the parenthesis or omit them. The following do not work:

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

GCC 9.2 would fail with:

main.cpp: In function ‘int main()’:
main.cpp:19:18: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘p (...)’, e.g. ‘(... ->* p) (...)’
   19 |     assert(c.*p(2) == 3);
      |

C++11 standard

.* and ->* are a single operators introduced in C++ for this purpose, and not present in C.

C++11 N3337 standard draft:

  • 2.13 "Operators and punctuators" has a list of all operators, which contains .* and ->*.
  • 5.5 "Pointer-to-member operators" explains what they do
怀里藏娇 2024-08-13 13:26:34
typedef void (Dog::*memfun)();
memfun doSomething = &Dog::bark;
....
(pDog->*doSomething)(); // if pDog is a pointer
// (pDog.*doSomething)(); // if pDog is a reference
typedef void (Dog::*memfun)();
memfun doSomething = &Dog::bark;
....
(pDog->*doSomething)(); // if pDog is a pointer
// (pDog.*doSomething)(); // if pDog is a reference
旧城烟雨 2024-08-13 13:26:34

我来这里是为了学习如何从方法创建函数指针(而不是方法指针),但这里的答案都没有提供解决方案。这是我的想法:

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++17,有一个更好的解决方案:

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

无需宏即可直接使用:

Dog dog;
using BarkFunction = void (*)(Dog*);
BarkFunction bark = MethodHelper<&Dog::bark>::call;
(*bark)(&dog); // or simply bark(&dog)

对于带有 const 等修饰符的方法,您可能需要一些更多的专业化,例如:

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

I came here to learn how to create a function pointer (not a method pointer) from a method but none of the answers here provide a solution. Here is what I came up with:

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>

So for your example you would now do:

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

Edit:
Using C++17, there is an even better solution:

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

which can be used directly without the macro:

Dog dog;
using BarkFunction = void (*)(Dog*);
BarkFunction bark = MethodHelper<&Dog::bark>::call;
(*bark)(&dog); // or simply bark(&dog)

For methods with modifiers like const you might need some more specializations like:

template <class C, class Ret, class... Args, Ret (C::*m)(Args...) const> struct MethodHelper<m> {
    static Ret call(const C* object, Args... args) {
        return (object->*m)(args...);
    }
};
霞映澄塘 2024-08-13 13:26:34

指向类成员的函数指针是一个非常适合使用 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);
}

A function pointer to a class member is a problem that is really suited to using boost::function. Small example:

#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);
}
半暖夏伤 2024-08-13 13:26:34

不能使用函数指针调用成员函数的原因是
普通函数指针通常只是函数的内存地址。

要调用成员函数,需要知道两件事:

  • 调用哪个成员函数
  • 应该使用哪个实例(谁的成员函数)

普通函数指针不能同时存储两者。使用C++成员函数指针
存储 a),这就是为什么在调用成员函数指针时需要显式指定实例。

Reason why you cannot use function pointers to call member functions is that
ordinary function pointers are usually just the memory address of the function.

To call a member function, you need to know two things:

  • Which member function to call
  • Which instance should be used (whose member function)

Ordinary function pointers cannot store both. C++ member function pointers are used
to store a), which is why you need to specify the instance explicitly when calling a member function pointer.

〆凄凉。 2024-08-13 13:26:34

要创建新对象,您可以使用placement new(如上所述),或者让您的类实现创建对象副本的clone() 方法。然后,您可以按照上面的说明使用成员函数指针调用此克隆方法来创建对象的新实例。克隆的优点是,有时您可能会使用指向基类的指针,但您不知道该对象的类型。在这种情况下,clone() 方法会更容易使用。另外,如果您想要的话,clone() 还可以让您复制对象的状态。

To create a new object you can either use placement new, as mentioned above, or have your class implement a clone() method that creates a copy of the object. You can then call this clone method using a member function pointer as explained above to create new instances of the object. The advantage of clone is that sometimes you may be working with a pointer to a base class where you don't know the type of the object. In this case a clone() method can be easier to use. Also, clone() will let you copy the state of the object if that is what you want.

萌面超妹 2024-08-13 13:26:34

我用 std::function 和 std::bind 做到了这一点。

我编写了这个 EventManager 类,它将处理程序向量存储在映射事件类型的 unordered_map 中(它们只是 const unsigned int,我有一个大的命名空间范围的枚举它们)到该事件类型的处理程序向量。

在我的 EventManagerTests 类中,我设置了一个事件处理程序,如下所示:

auto delegate = std::bind(&EventManagerTests::OnKeyDown, this, std::placeholders::_1);
event_manager.AddEventListener(kEventKeyDown, delegate);

这是 AddEventListener 函数:

std::vector<EventHandler>::iterator EventManager::AddEventListener(EventType _event_type, EventHandler _handler)
{
    if (listeners_.count(_event_type) == 0) 
    {
        listeners_.emplace(_event_type, new std::vector<EventHandler>());
    }
    std::vector<EventHandler>::iterator it = listeners_[_event_type]->end();
    listeners_[_event_type]->push_back(_handler);       
    return it;
}

这是 EventHandler 类型定义:

typedef std::function<void(Event *)> EventHandler;

然后回到 EventManagerTests::RaiseEvent,我执行以下操作:

Engine::KeyDownEvent event(39);
event_manager.RaiseEvent(1, (Engine::Event*) & event);

这是 EventManager::RaiseEvent 的代码:

void EventManager::RaiseEvent(EventType _event_type, Event * _event)
{
    if (listeners_.count(_event_type) > 0)
    {
        std::vector<EventHandler> * vec = listeners_[_event_type];
        std::for_each(
            begin(*vec), 
            end(*vec), 
            [_event](EventHandler handler) mutable 
            {
                (handler)(_event);
            }
        );
    }
}

这是有效的。我在 EventManagerTests::OnKeyDown 中接到电话。我必须在清理时间删除向量,但是一旦我这样做了,就不会出现泄漏。在我的计算机上引发一个事件大约需要 5 微秒,那是在 2008 年左右。并不完全是超级快,但是。只要我知道这一点并且我不在超热门代码中使用它就足够了。

我想通过滚动我自己的 std::function 和 std::bind 来加速它,也许使用数组数组而不是向量的 unordered_map ,但我还没有完全弄清楚如何存储成员函数指针并从对被调用的类一无所知的代码中调用它。睫毛的回答看起来很有趣..

I did this with std::function and std::bind..

I wrote this EventManager class that stores a vector of handlers in an unordered_map that maps event types (which are just const unsigned int, I have a big namespace-scoped enum of them) to a vector of handlers for that event type.

In my EventManagerTests class, I set up an event handler, like this:

auto delegate = std::bind(&EventManagerTests::OnKeyDown, this, std::placeholders::_1);
event_manager.AddEventListener(kEventKeyDown, delegate);

Here's the AddEventListener function:

std::vector<EventHandler>::iterator EventManager::AddEventListener(EventType _event_type, EventHandler _handler)
{
    if (listeners_.count(_event_type) == 0) 
    {
        listeners_.emplace(_event_type, new std::vector<EventHandler>());
    }
    std::vector<EventHandler>::iterator it = listeners_[_event_type]->end();
    listeners_[_event_type]->push_back(_handler);       
    return it;
}

Here's the EventHandler type definition:

typedef std::function<void(Event *)> EventHandler;

Then back in EventManagerTests::RaiseEvent, I do this:

Engine::KeyDownEvent event(39);
event_manager.RaiseEvent(1, (Engine::Event*) & event);

Here's the code for EventManager::RaiseEvent:

void EventManager::RaiseEvent(EventType _event_type, Event * _event)
{
    if (listeners_.count(_event_type) > 0)
    {
        std::vector<EventHandler> * vec = listeners_[_event_type];
        std::for_each(
            begin(*vec), 
            end(*vec), 
            [_event](EventHandler handler) mutable 
            {
                (handler)(_event);
            }
        );
    }
}

This works. I get the call in EventManagerTests::OnKeyDown. I have to delete the vectors come clean up time, but once I do that there are no leaks. Raising an event takes about 5 microseconds on my computer, which is circa 2008. Not exactly super fast, but. Fair enough as long as I know that and I don't use it in ultra hot code.

I'd like to speed it up by rolling my own std::function and std::bind, and maybe using an array of arrays rather than an unordered_map of vectors, but I haven't quite figured out how to store a member function pointer and call it from code that knows nothing about the class being called. Eyelash's answer looks Very Interesting..

月野兔 2024-08-13 13:26:34

作为一种解决方法/技巧如果您可以控制 C API,您通常可以在 C 回调签名中放置一个 void* 额外参数,然后您将强制转换它(reinterpret_cast) 达到您的预期。

As a workaround/trick if you have control over the C API you could normally put a void* extra argument inside the C callback signature, afterward you will cast it (reinterpret_cast) to what you would expect.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文