通过函数指针调用C ++类的方法

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

class Dog : Animal { Dog (); void bark (); } … Dog* pDog = new Dog (); BarkFunction pBark = &Dog::bark; (*pBark) (pDog); … 

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

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

这是可能的,如果是这样,那么这样做的首选方法是什么?

阅读详细信息:

// 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开始最容易。 对于成员函数,在types声明中添加类名:

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

现在,虽然通常可以在Animal*的地方使用Dog* ,但多亏了多态的魔力,函数指针的types并不遵循类层次结构的查找规则。 所以动物方法指针与Dog方法指针不兼容,换句话说,您不能将Dog* (*)()赋值给Animal* (*)()types的variables。

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

回答了上面的问题,我想知道是否没有更好的方法来实现你所需要的。 有几个特定的​​情况下,你会做这种事情,但你可能会发现有其他模式,更好地为您的问题。 如果你用更一般的术语来描述你正在努力达到的目标,那么蜂巢式的思维可能会更有用!

与上面相关,您无疑会发现Boost绑定库和其他相关模块非常有用。

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

成员函数指针不是简单的函数指针。 在实现方面,编译器不能使用一个简单的函数地址,因为一般来说,除非知道哪个对象需要解引用(认为是虚函数),否则不知道要调用的地址。 当然,您也需要知道该对象以提供this隐式参数。

说了你需要他们,现在我会说你真的需要避免他们。 严重的是,成员指针是一个痛苦。 看看实现相同目标的面向对象的devise模式,或者使用boost::function或者上面提到的任何东西,假设你做出这个select,那就更加理智了。

如果你正在向现有的代码提供这个函数指针,所以你真的需要一个简单的函数指针,你应该写一个函数作为类的静态成员。 静态成员函数不能理解this ,所以你需要传入一个明确的参数。 曾经有一个非常规的习惯用法,用于处理需要函数指针的旧C代码

 class myclass { public: virtual void myrealmethod () = 0; static void myfunction (myclass *p); } void myclass::myfunction (myclass *p) { p->myrealmethod (); } 

由于myfunction实际上只是一个普通的函数(不考虑范围问题),所以可以用普通的C方法find一个函数指针。

编辑 – 这种方法被称为“类方法”或“静态成员函数”。 与非成员函数的主要区别在于,如果从类外部引用它,则必须使用:: scopeparsing运算符指定范围。 例如,要获取函数指针,使用&myclass::myfunction并调用它使用myclass::myfunction (arg);

当使用原来为C而不是C ++devise的Win32 API时,这种事情是相当普遍的。 当然在这种情况下,参数通常是LPARAM或类似的,而不是一个指针,需要一些强制转换。

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

我来这里学习如何从一个方法创build一个函数指针(不是方法指针),但是这里没有答案提供了一个解决scheme。 所以我想了一下,发现了一个我认为值得分享的好方法:

 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 ++成员函数指针用于存储a),这就是为什么当调用成员函数指针时需要显式指定实例的原因。

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

最小的可运行示例

 #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“指针到成员操作符”解释了它们所做的事情

如果你想调用一个对象成员函数,然后只是传递一个指向对象的指针,我还是不太了解'为什么'? 如果有人抱怨说,因为它可以让你更好地封装类,为什么不做一个所有类inheritance的接口类?

要创build一个新对象,您可以使用placement new,如上所述,或者让您的类实现一个clone()方法来创build对象的副本。 然后,您可以使用成员函数指针调用此克隆方法,如上所述创build该对象的新实例。 克隆的好处在于,有时你可能正在使用一个指向基类的指针,在这个基类中你不知道对象的types。 在这种情况下,clone()方法可以更容易使用。 另外,clone()会让你复制对象的状态,如果这是你想要的。