0

我有两个热门课程。假设其中一个是图像,另一个是页面。Image 对象收到一个 OnPointerPressed 事件,当它发生时,我想从 Page calss 调用一个函数。问题是直到我使用 union hack 将成员函数的地址传递给图像时才定义此函数。当从 Image 类调用此方法时,它可以正常工作,但在方法内部我看不到 Page Object 及其成员。

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());
    }
}
4

2 回答 2

1

在代码的汇编语言版本和非汇编语言版本中,您都错误地调用了成员函数。成员函数有特定要求,不适用于调用自由函数(和静态成员函数),这正是您的代码的两个版本正在执行的操作。调用非静态成员函数时,this指针作为第一个参数隐式传递。这可以通过将指针值压入堆栈、将其放入寄存器或两者兼而有之来完成——这实际上取决于编译器如何生成调用代码。

当前版本的代码尝试调用成员函数,就好像它是一个自由函数一样,但将对象指针传递到堆栈上。这是一个非常糟糕的主意,也是值this不正确的原因。ARG如果是通过值而不是通过引用或指针传递的对象,它也可能无法按预期工作。这是因为需要将整个对象复制到堆栈上——甚至不要自己尝试这样做。相反,您应该使用该语言提供的功能,这些功能专门允许您通过指针调用成员函数。

指向成员函数的指针的语法是returntype (ClassType::*)(parameters). 通过使用正确的语法,您可以依靠编译器在您错误地使用它时告诉您,而不是像您目前正在做的那样尝试寻找晦涩难懂的错误。你不仅应该在调用成员函数时使用它,还应该使用它来将指针存储在列表中。VC++ 允许您将函数指针转换为 void 指针,但这是 Microsoft 对该语言的扩展,不是标准 C++。void我建议你尽可能避免。

需要进行的一项更改是调用成员函数的方式。这与使用指向自由函数的指针不同,下面显示了正确的语法。

(objectpointer->*functionpointer)(arguments);

由于运算符优先级,需要额外的一组括号,并且->*是“指向成员的指针”运算符。下面的代码显示了您需要对代码进行的更改才能正确调用成员函数。

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()包含未定义的行为。C++ 语言标准 $9.5/1 起

在一个 union 中,任何时候最多可以有一个数据成员处于活动状态,即任何时候最多可以将一个数据成员的值存储在 union 中。

add()你设置的值t然后读取的值v_ptr

于 2013-06-02T16:32:52.720 回答
0

您还需要传入“this”指针以使其工作,否则它将实例化该类的单独实例:

您必须像这样修改 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);

并且在内部它应该通过调用传递“this”值的指向函数。这现在不起作用的原因是,如果函数像静态函数一样被实例化,那么它将成为新对象的一部分,因此不会保留旧对象的状态。

PS:您应该粘贴更多代码,以便为回答此问题的人提供更好的主意

于 2013-06-02T07:32:49.250 回答