gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-29 20:42:18 25 4
gpt4 key购买 nike

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

#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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com