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

Ton*_*ony 105 c++ function-pointers class-method

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

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

…
Dog* pDog = new Dog ();
BarkFunction pBark = &Dog::bark;
(*pBark) (pDog);
…
Run Code Online (Sandbox Code Playgroud)

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

NewAnimalFunction pNew = &Dog::Dog;
Animal* pAnimal = (*pNew)();    
Run Code Online (Sandbox Code Playgroud)

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

Sat*_*bir 114

阅读这个详细:

// 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');
Run Code Online (Sandbox Code Playgroud)

  • 令人惊讶的是他们决定这样做:`*this.*pt2Member`会起作用.`*`优先于`.*`...就个人而言,我仍然会写'this - >*pt2Member`,这是一个较少的运算符. (17认同)
  • 为什么要将`pt2ConstMember`初始化为`NULL`? (6认同)

gav*_*inb 55

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

最简单的开始是typedef.对于成员函数,在类型声明中添加类名:

typedef void(Dog::*BarkFunction)(void);
Run Code Online (Sandbox Code Playgroud)

然后要调用该方法,您使用->*运算符:

(pDog->*pBark)();
Run Code Online (Sandbox Code Playgroud)

另外,如果可能的话,我也想通过指针调用构造函数.这是可能的,如果是这样,那么首选的方法是什么?

我不相信你可以和这样的建设者合作 - 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;
}
Run Code Online (Sandbox Code Playgroud)

现在虽然您通常可以使用a Dog*代替Animal*多态性的魔力,但函数指针的类型遵循类层次结构的查找规则.因此,Animal方法指针与Dog方法指针不兼容,换句话说,您不能将a指定给Dog* (*)()类型的变量Animal* (*)().

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

回答上述问题后,我想知道是否有更好的方法来实现您的需求.有一些特定的场景,你会做这种事情,但你可能会发现其他模式更适合你的问题.如果你用更一般的术语描述你想要达到的目标,那么蜂巢头脑可能会更有用!

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

  • 我已经使用C++超过10年了,并且定期学习新东西.我之前从未听说过` - >*`,但现在我希望我永远不需要它:) (8认同)

Ste*_*314 31

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

函数的成员指针不仅仅是函数指针.在实现方面,编译器不能使用简单的函数地址,因为通常,在知道要取消引用哪个对象(想想虚函数)之前,您不知道要调用的地址.当然,您还需要知道对象以提供this隐式参数.

说过你需要它们,现在我会说你真的需要避开它们.说真的,成员指针很痛苦.看看面向对象的设计模式,实现相同的目标,或使用boost::function上面提到的或其他任何东西,假设你做出这样的选择,那就更加明智了.

如果要提供指向现有代码的函数指针,那么您确实需要一个简单的函数指针,您应该将函数编写为类的静态成员.静态成员函数无法理解this,因此您需要将对象作为显式参数传递.对于需要函数指针的旧C代码,这些行中曾经有一个不那么不寻常的习惯用法

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

    static void myfunction (myclass *p);
}

void myclass::myfunction (myclass *p)
{
  p->myrealmethod ();
}
Run Code Online (Sandbox Code Playgroud)

由于myfunction实际上只是一个普通函数(除了范围问题),因此可以以正常的C方式找到函数指针.

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

当使用旧的Win32 API时,这种情况相当普遍,这些API最初是为C而不是C++设计的.当然,在这种情况下,参数通常是LPARAM或类似的而不是指针,并且需要一些转换.

  • 建议使用提升是严苛的.使用方法指针有很多实际的理由.我不介意将提升作为一种替代方案,但当有人说其他人应该在不知道所有事实的情况下使用它时,我会感到讨厌.提升需要付出代价!如果这是一个嵌入式平台,那么它可能不是一个可能的选择.除此之外,我真的很喜欢你的写作. (3认同)

Ara*_*raK 18

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

  • 应该是:(pDog - >*doSomething)(); //如果pDog是一个指针//(pDog.*doSomething)(); //如果pDog是引用,因为()运算符具有更高的优先级,然后是 - >*和.*. (2认同)

eye*_*ash 10

我来到这里是为了学习如何从方法创建函数指针(不是方法指针),但这里没有一个答案提供解决方案.所以我想到了它并找到了一个很好的解决方案,我认为值得分享:

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>
Run Code Online (Sandbox Code Playgroud)

因此,对于您的示例,您现在可以:

Dog dog;
using BarkFunction = void (*)(Dog*);
BarkFunction bark = METHOD_FP(&Dog::bark);
(*bark)(&dog); // or simply bark(&dog)
Run Code Online (Sandbox Code Playgroud)


Cir*_*四事件 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);
}
Run Code Online (Sandbox Code Playgroud)

您无法更改括号的顺序或省略它们.以下不起作用:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out
Run Code Online (Sandbox Code Playgroud)

C++ 11标准

.*并且->*是为此目的而在C++中引入的单个运算符,并且不存在于C.

C++ 11 N3337标准草案:

  • 2.13"运算符和标点符号"包含所有运算符的列表,其中包含.*->*.
  • 5.5"指向成员的操作员"解释了他们的工作


hrn*_*rnt 6

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

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

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

普通函数指针不能同时存储.C++成员函数指针用于存储a),这就是为什么在调用成员函数指针时需要显式指定实例的原因.


Ben*_*min 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);
}
Run Code Online (Sandbox Code Playgroud)