c++ - C++如何识别使用哪个函数(类成员函数指针相关)

标签 c++ function class pointers member

我有一个关于类成员函数的问题。这涉及继承。我在下面写了下面的代码,但我并不真正理解它是如何工作的(我只能猜测):

#include <iostream>

using namespace std;

class Base
{
};

typedef void(Base::*handler)();

#define selector(_SELECTOR) static_cast<handler>(&_SELECTOR)

class Boo : public Base
{
public:
    void callingFunc()
    {
        cout << "Hey there" << endl;
    }

};


class Foo
{   
public:
    void setCallback( Base * instance,  void (Base::*funcToCall)(void) )
    {
        this->instance = instance;
        this->funcToCall = funcToCall;
    }

    void doCall()
    {
        (instance->*funcToCall)();
    }

private:
    Base* instance;
    void (Base::*funcToCall)(void);
};

void main()
{
    Foo * foo = new Foo();
    Boo * boo = new Boo();

    foo->setCallback(boo, selector(Boo::callingFunc) );
    foo->doCall();  // outputs "Hey there"
}

此代码有效,但我想知道详细原因。 doCall() 似乎将 funcToCall(void (基地::*)(无效))。它还似乎将我的 instance 变量转换为 Boo!它似乎神奇地知道我给出的 funcToCall 属于 Boo 并进行相应的转换。

这是怎么做到的?这是在运行时还是编译时完成的?当我调用 (instance->*funcToCall)(); 时,它是否只是尝试查找函数的名称?

不要在 typedef 问题上挑剔我。我知道有些东西需要 typedef 以提高可读性。这只是测试代码。

编辑: 我对代码进行了更多尝试,它看起来很奇怪。我添加了一个新类,结果如下:

class Goo : public Base
{
public:
    void callingFunc()
    {
        cout << "Yo there" << endl;
    }
};

void main()
{
    Foo * foo = new Foo();
    Boo * boo = new Boo();
    Goo * goo = new Goo();

    foo->setCallback(goo, selector(Boo::callingFunc) );
    foo->doCall(); // outputs "Hey there" not "Yo there"
}

在这一点上,它有点说得通,但也有点说不通。我的意思是,很明显它会从 Boo 调用“Hey There”,但为什么代码没有爆炸?看起来很危险。

编辑 2: 发现了一些真正令人不安和不安的东西。我调整了代码,接受一个计数器,这样我就有一个变量来检查发生了什么。

#include <iostream>

using namespace std;

class Base
{
};

typedef void(Base::*handler)();
#define selector(_SELECTOR) static_cast<handler>(&_SELECTOR)

class Boo : public Base
{
public:
    Boo() : counter(0) {}
    void callingFunc()
    {
        cout << "Hey there " << counter << " at " << &counter << endl;
        counter += 1;
    }

    int counter;

};

class Goo : public Base
{
public:
    Goo() : counter(0) {}
    void callingFunc()
    {
        cout << "Yo there " << counter << " at " << &counter << endl;
        counter += 1;
    }

    int counter;
};

class Foo
{
public:
    void setCallback( Base * instance,  void (Base::*funcToCall)(void) )
    {
        this->instance = instance;
        this->funcToCall = funcToCall;
    }

    void doCall()
    {
        (instance->*funcToCall)();
    }

private:
    Base* instance;
    void (Base::*funcToCall)(void);
};

void main()
{
    Foo * foo = new Foo();
    Boo * boo = new Boo();
    Base * base = new Base();
    Goo * goo = new Goo();

    // first run
    foo->setCallback(goo, selector(Boo::callingFunc) );
    foo->doCall(); // "Hey there 0 at 0044BC60"

    foo->setCallback(boo, selector(Boo::callingFunc) );
    foo->doCall(); // "Hey there 0 at 0044BC00"

    //second run
    foo->setCallback(goo, selector(Boo::callingFunc) );
    foo->doCall(); // "Hey there 1 at 0044BC60"

    foo->setCallback(boo, selector(Boo::callingFunc) );
    foo->doCall(); // "Hey there 1 at 0044BC00"

    // attempt with base
    foo->setCallback(base, selector(Boo::callingFunc) );
    foo->doCall(); // "Hey there *rubbish number* at  at 0044BC30"

}

现在我非常确定函数回调是运行时的事情(显然是因为它不会给出编译错误,但我不确定,因为通常情况并非如此)。如果它是运行时的东西,那么是的,它有点有意义,因为它几乎像脚本语言一样工作(按名称查找变量,如果存在则更新它,等等)。

我还需要有人来确认这一点。它真的看起来既强大又危险。自从我看到这样的东西已经有一段时间了。我现在太忙了,无法尝试打开程序集中的东西来破译到底发生了什么。另外,我不擅长阅读它^^;;

EDIT 3 谢谢你们,现在一切都明白了。 villekulla 的回复让我相信,因为我的 Boo 和 Goo 类的结构相同,所以它能够以相同的方式访问“计数器”变量(如果您了解类和结构的内存是如何分配的,这应该是显而易见的)。所以我在 Goo 中插入了一个“char”变量:

class Goo : public Base
{
public:
    Goo() : counter(0) {}
    void callingFunc()
    {
        cout << "Yo there " << counter << " at " << &counter << endl;
        counter += 1;
    }

    char hey;
    int counter;
};

调用:

foo->setCallback(goo, selector(Boo::callingFunc) );
foo->doCall();

twice 会产生乱码,因为它正在抓取计数器应该在的字符(确认未定义的行为,如前所述)。没有编译错误,因为......好吧......就编译而言,代码没有任何严重错误。

再次感谢!

最佳答案

这是未定义的行为。

考虑向 Foo 和 Goo 添加一个成员并调整 callingFunc 以使用该成员:

class Boo : public Base
{
public:
  Boo()
    : m("Boo")
  {}
  void callingFunc()
  {
     cout << "Hey there, I'am " << boo << endl;
  }
  const char* m;
};

class Goo : public Base
{
public:
  Goo()
    : m("Goo")
  {}
  void callingFunc()
  {
     cout << "Yo there, I'am " << goo << endl;
  }
  const char* m;
};

在案例中

foo->setCallback(boo, selector(Boo::callingFunc) );

你得到输出

Hey there, I'am Boo

在这种情况下

foo->setCallback(goo, selector(Boo::callingFunc) );

你得到输出

Hey there, I'am Goo

你清楚地看到 Boo::callingFunc 得到了 Goo 的一些实例......

这是 C++ 搬起石头砸自己脚的数百万个例子之一……只做标准允许的事情:/

你的例子并没有爆炸,因为 callingFunc 和 Goo/Foo 是微不足道的。如果你不幸它永远不会爆炸,它“只会”引入奇怪的错误(Foo::callingFunc 处理 Goo::callingFunc 的数据)。

因为您没有使用虚函数,所有函数调用地址都在编译时获取地址时解析(在 foo->setCallback(boo, selector(Boo::callingFunc) 行);

关于c++ - C++如何识别使用哪个函数(类成员函数指针相关),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19087588/

相关文章:

c++ - 此代码是否有效的 C++?

function - 如何在Lua中的函数内调用函数?

c++ - 在循环内/外创建对象 : allocation and speed

c++ - 如何访问在另一个类中声明的类?

class - 从 iPhone Storyboard转换为大小类 xcode 6

C++ 如何通过套接字发送对象?

c++ - 在 Vector 内的对象中调用函数指针

c++ - C++11 std::function 是否限制函数指针可以具有的参数数量?

javascript - 通过a标签中的jquery函数设置点击时某些div的高度

javascript - 举升状态不工作