如何创建指向成员函数的指针并调用它?
如何获取类成员函数的函数指针,然后使用特定对象调用该成员函数?我想写:
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
阅读此了解详细信息:
Read this for detail :
从
typedef
开始是最简单的。对于成员函数,您可以在类型声明中添加类名:然后要调用该方法,您可以使用
->*
运算符:我不相信你可以使用这样的构造函数 - ctors 和 dtors 很特别。实现此类事情的正常方法是使用工厂方法,它基本上只是一个为您调用构造函数的静态函数。请参阅下面的代码示例。
我已经修改了您的代码,基本上可以按照您所描述的方式进行操作。下面有一些注意事项。
现在,尽管由于多态性的魔力,您通常可以使用
Dog*
代替Animal*
,但函数指针的类型却不能 em> 遵循类层次结构的查找规则。因此 Animal 方法指针与 Dog 方法指针不兼容,换句话说,您不能将Dog* (*)()
分配给Animal* (*) 类型的变量()
。静态 newDog 方法是工厂的一个简单示例,它只是创建并返回新实例。作为一个静态函数,它有一个常规的 typedef (没有类限定符)。
回答完上述问题后,我确实想知道是否没有更好的方法来实现您的需求。在一些特定的场景中,您会执行此类操作,但您可能会发现还有其他模式更适合您的问题。如果你用更笼统的术语描述你想要实现的目标,蜂巢思维可能会更有用!
与上述相关,您无疑会找到 Boost bind 库和其他相关模块非常有用。
It's easiest to start with a
typedef
. For a member function, you add the classname in the type declaration:Then to invoke the method, you use the
->*
operator: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.
Now although you can normally use a
Dog*
in the place of anAnimal*
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 aDog* (*)()
to a variable of typeAnimal* (*)()
.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 regulartypedef
(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.
我认为没有人在这里解释过一个问题是你需要“成员指针" 而不是普通的函数指针。
指向函数的成员指针不仅仅是函数指针。在实现方面,编译器不能使用简单的函数地址,因为通常,在知道要取消引用哪个对象之前,您不知道要调用的地址(想想虚拟函数)。当然,您还需要知道该对象才能提供
this
隐式参数。说了你需要它们,现在我要说你确实需要避免它们。说真的,成员指针很痛苦。查看实现相同目标的面向对象的设计模式,或者使用 boost::function 或上面提到的任何内容,要明智得多 - 假设您可以做出这样的选择,即。
如果您向现有代码提供该函数指针,那么您确实需要一个简单的函数指针,那么您应该将函数编写为类的静态成员。静态成员函数不理解
this
,因此您需要将对象作为显式参数传递。曾经有一个不太不寻常的习惯用法,用于处理需要函数指针的旧 C 代码,因为
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 pointersSince
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 usemyclass::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.
最小可运行示例
main.cpp
编译并运行:
在 Ubuntu 18.04 中测试。
您不能更改括号的顺序或省略它们。以下内容不起作用:
GCC 9.2 将失败:
C++11 标准
.*
和->*
are a 为此目的在 C++ 中引入了单个运算符,但在 C 中不存在。C++11 N3337 标准草案:
.*
和-> ;*
。Minimal runnable example
main.cpp
Compile and run:
Tested in Ubuntu 18.04.
You cannot change the order of the parenthesis or omit them. The following do not work:
GCC 9.2 would fail with:
C++11 standard
.*
and->*
are a single operators introduced in C++ for this purpose, and not present in C.C++11 N3337 standard draft:
.*
and->*
.我来这里是为了学习如何从方法创建函数指针(而不是方法指针),但这里的答案都没有提供解决方案。这是我的想法:
因此,对于您的示例,您现在要做的就是:
编辑:
使用 C++17,有一个更好的解决方案:
无需宏即可直接使用:
对于带有
const
等修饰符的方法,您可能需要一些更多的专业化,例如: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:
So for your example you would now do:
Edit:
Using C++17, there is an even better solution:
which can be used directly without the macro:
For methods with modifiers like
const
you might need some more specializations like:指向类成员的函数指针是一个非常适合使用 boost::function 的问题。小例子:
A function pointer to a class member is a problem that is really suited to using boost::function. Small example:
不能使用函数指针调用成员函数的原因是
普通函数指针通常只是函数的内存地址。
要调用成员函数,需要知道两件事:
普通函数指针不能同时存储两者。使用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:
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.
要创建新对象,您可以使用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.
我用 std::function 和 std::bind 做到了这一点。
我编写了这个 EventManager 类,它将处理程序向量存储在映射事件类型的 unordered_map 中(它们只是 const unsigned int,我有一个大的命名空间范围的枚举它们)到该事件类型的处理程序向量。
在我的 EventManagerTests 类中,我设置了一个事件处理程序,如下所示:
这是 AddEventListener 函数:
这是 EventHandler 类型定义:
然后回到 EventManagerTests::RaiseEvent,我执行以下操作:
这是 EventManager::RaiseEvent 的代码:
这是有效的。我在 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:
Here's the AddEventListener function:
Here's the EventHandler type definition:
Then back in EventManagerTests::RaiseEvent, I do this:
Here's the code for EventManager::RaiseEvent:
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..
作为一种解决方法/技巧如果您可以控制 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.