我有一个关于类成员函数的问题。这涉及继承。我在下面编写了以下代码,但是我并不能完全理解它的工作原理(我只能猜测):
#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 (Boo::*)(void))
的(void (Base::*)(void))
。它似乎也将我的instance
变量转换成Boo
!似乎魔术般地知道funcToCall
我给的I属于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"
}
现在我很确定函数回调是运行时的事情(显然是因为它不会产生编译错误,但是我不确定,因为通常情况并非如此)。如果它是运行时的东西,那么是的,因为它的工作原理几乎类似于脚本语言(按变量名查找变量,如果存在则对其进行更新,等等),这是有意义的。
我仍然需要有人确认。同时看起来确实强大而危险。自从我看到这样的东西以来已经有一段时间了。我现在太忙了,无法尝试打开组装的东西来解释正在发生的事情。另外,我不擅长阅读^^ ;;
编辑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();
两次将产生乱码,因为它抓住了计数器应放在的字符(确认未定义的行为,如前所述)。没有编译错误,因为……就编译而言,代码没有什么大不了的。
再次感谢!
这是未定义的行为。
考虑将成员添加到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) );
你得到的输出
嘿,我是Boo
而在这种情况下
foo->setCallback(goo, selector(Boo::callingFunc) );
你得到的输出
嘿,我是Goo
您清楚地看到Boo :: callingFunc得到了Goo的支持...
这是C ++如何帮助脚步射击的数百万个例子之一...仅做标准允许的事情:/
您的示例并未爆炸,因为调用Func和Goo / Foo无关紧要。而且,如果不幸的是它永远不会爆炸,那么它“只会”会引入奇怪的错误(Foo :: callingFunc处理Goo :: callingFunc的数据)。
因为您没有使用虚函数,所以所有函数调用地址都在获取地址时在编译时解析(在foo-> setCallback(boo,selector(Boo :: callingFunc))行中);
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句