我有两门课。假设其中一个是Image,另一个是Page。 Image对象接收一个OnPointerPressed事件,当它发生时,我想从Page calss调用一个函数。问题是直到我使用union hack将成员函数的地址传递给图像时才定义此函数。当从Image类调用此方法时,它工作正常但在方法内部我看不到页面对象及其成员。
MainPage::MainPage(void)
{
//.......
image1->OnPointerPressedHandler->add(&MainPage::imageMousePressed);
}
void MainPage::imageMousePressed(STObject* sender, STPointerEventArg * args)
{
void * dsda = this; //the address of this is something wierd
pres = true;
}
有人可以帮忙吗?
template<class T>
void add(T const & x)
{
union hlp_t{
void * v_ptr;
T t;
};
hlp_t a;
a.t = x;
functions->addLast(a.v_ptr);
}
template<class ARG>
void trigger(SENDERTYPE sender, ARG arg)
{
STLinkedListIterator<void *>* it = functions->createIteator();
if(it != nullptr)
{
do
{
void (*callback)(SENDERTYPE,ARG) =static_cast<void (*)(SENDERTYPE,ARG)>(it->getData());
callback(sender,arg);
/*
void * myVoid = it->getData();
__asm
{
push [arg];
push [sender];
call [myVoid];
}
*/
it->next();
}while(!it->EOL());
}
}
答案 0 :(得分:1)
在汇编语言版本和代码的非汇编语言版本中,您正在调用成员函数 。成员函数的特定要求不适用于调用自由函数(和静态成员函数),这正是代码的两个版本正在执行的操作。调用非静态成员函数时,this
指针将作为第一个参数隐式传递。这可以通过将指针值推入堆栈,将其放入寄存器或两者来完成 - 这实际上取决于编译器如何生成调用代码。
当前版本的代码尝试调用成员函数,就像它是一个自由函数一样,但是传递堆栈上的对象指针。这是一个非常错误的想法,也是this
的值不正确的原因。如果ARG
是通过值而不是通过引用或指针传递的对象,它也可能无法正常工作。这是因为需要将整个对象复制到堆栈中 - 甚至不要自己尝试这样做。相反,您应该使用特定语言提供的功能,以允许您通过指针调用成员函数。
指向成员函数的指针的语法是returntype (ClassType::*)(parameters)
。通过使用正确的语法,您可以依赖编译器在错误地使用它时告诉您,而不是像您当前那样尝试查找模糊的错误。调用成员函数时不仅应该使用它,还应该使用它来将指针存储在列表中。 VC ++允许您将函数指针转换为void指针,但这是该语言的Microsoft扩展,并且不是标准C ++。我建议您尽可能避免使用void
。
您将需要进行的一项更改是您调用成员函数的方式。这与使用指向自由函数的指针不同,以下显示正确的语法。
(objectpointer->*functionpointer)(arguments);
由于operator precedence而需要额外的括号集,而->*
是“指向成员的指针”运算符。下面的代码显示了您需要对正确调用成员函数的代码所做的更改。
template<class ARG>
void trigger(STObject* sender, ARG arg)
{
STLinkedListIterator<void *>* it = functions->createIteator();
if(it != nullptr)
{
do
{
// cast to a pointer to member function so the compiler knows
// the correct type of the function call.
void (STObject::*callback)(ARG)
= static_cast<void (STObject::*)(ARG)>(it->getData());
// call it using the correct syntax and let the compiler
// handle all the gory details.
(sender->*callback)(arg);
it->next();
}while(!it->EOL());
}
}
您可以通过添加其他模板参数来指定目标对象的类型来使其更通用。
template<class SENDER, class ARG>
void trigger(SENDER* sender, ARG arg)
{
STLinkedListIterator<void *>* it = functions->createIteator();
if(it != nullptr)
{
do
{
void (SENDER::*callback)(ARG)
= static_cast<void (SENDER::*)(ARG)>(it->getData());
(sender->*callback)(arg);
it->next();
}while(!it->EOL());
}
}
作为旁注,函数add()
包含undefined behavior。从$ 9.5 / 1的C ++语言标准
在联盟中,最多其中一个数据成员可以随时处于活动状态,也就是说,最多一个数据成员的值可以随时存储在一个联合中
在add()
中设置t
的值,然后读取v_ptr
的值。
答案 1 :(得分:0)
你需要传递&#39;这个&#39;指针也可以使这个工作,否则它会实例化一个单独的类实例:
您必须像这样修改imageMousePressed:
// add in a void* argument
void MainPage::imageMousePressed(STObject* sender, STPointerEventArg * args, void* mainPageObject)
{
MainPage* dsda = (MainPage*) mainPageObject; // now, you should have a pointer to the valid object
pres = true;
}
现在,这意味着还应该修改OnPointerPressedHandler的add函数以适应这个:
image1->OnPointerPressedHandler->add(&MainPage::imageMousePressed, this);
在内部它应该通过调用指向函数传递&#39;这个&#39;值。这个现在不起作用的原因,如果函数被实例化为静态函数,然后它将成为新对象的一部分,那么旧对象的状态将不会被保留。
PS:您应该粘贴更多代码,以便更好地了解回答此问题的人