C ++,指针到函数和指向成员函数的指针之间的等价?

时间:2009-01-04 15:08:46

标签: c++ boost function-pointers

我习惯将成员函数视为普通函数的一个特例,其中成员函数在'this'指针的参数列表的开头有一个额外的参数,即,成员函数应该行动。我过去一直使用boost :: function,从未遇到过任何问题:

boost::function f<(void)(MyObject*, int, int)> = &MyObject::method_that_takes_two_ints;

但是我已经看到了成员函数指针的这种语法:

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

在此语法中,'this'参数不可见。这让我想知道引擎盖指针到成员函数是否真的是一个单独的野兽,而且这种提升正在为我处理细节。

标准规定了'this'参数的位置是什么?也许只是在我的编译器上,额外的'this'参数首先出现,也许在其他编译器上它可能在最后?我很幸运,我的思维方式与我的编译器(GCC4,VS2005)如何处理它一致?指向成员函数的指针总是只是带有额外参数的指针到函数的特殊情况,或者编译器能够以不同的方式实现它们吗?

9 个答案:

答案 0 :(得分:10)

该标准几乎没有说明应放置this指针的位置,事实上,对成员函数使用不同的调用约定是相当常见的。 (所以'this'指针不仅仅是一个额外的第一个参数,它实际上存储在与第一个arg通常不同的位置)

特别是,MSVC对成员函数使用thiscall调用约定,在其他地方使用stdcallhttp://www.hackcraft.net/cpp/MSCallingConventions/#thiscall描述了它们之间的差异,但请注意thiscallthis指针存储在ECX寄存器中,而stdcall存储所有堆栈上的参数。

你最好将它们视为完全不同的类型。指向成员函数的指针只是指向具有额外参数的函数的指针。

答案 1 :(得分:9)

this指针不存储在指向成员的指针中(成员函数指针是这种情况的特例)。如果你这样做

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

然后存储的只是在objet上调用哪个成员函数的信息,您稍后必须提供该信息。如果要调用它,则必须传递一个对象,编译器将从该对象获取this指针。

MyObject o; (o.*f)(1, 2);

成员函数指针只是一个成员指针,其类型(指向的)是一个函数类型。标准说成员函数指针没有自己指向的“成员函数类型”,并且会以某种方式包含this指针类型。

int main() {
    typedef void fun() const;
    fun MyObject::*mem_function_ptr = 
        &MyObject::const_method_that_takes_two_ints;
}
该代码中的

fun是函数类型。 “正常”函数具有的类型。与成员函数指针相反,指向函数的指针只是指向具有该类型的函数的指针:

void foo() { cout << "hello"; }
int main() {
    typedef void fun();
    fun * f = &foo;
}

指向成员函数的指针在该函数类型的顶部有额外的成员指针级别。

关于this指针的一些内容以及它与它所指向的对象的关系(不是技术性的,只是理论上的东西):

每个成员函数都有一个名为implicit object parameter的隐藏参数,其类型为MyObject&MyObject const&,具体取决于您是否具有const或nonconst成员函数。您在o上调用成员函数的对象是implied object argument,它将传递给参数。在构成描述如何调用成员函数的规则的标准理论中,隐式对象参数是第一个隐藏参数。这是概念性的,并不意味着它是实现中的真实案例。然后将隐含的对象参数绑定到该隐式对象参数,可能导致隐式转换(因此,如果在非const对象上调用const成员函数,则限定转换将从MyObject转换为MyObject const&。这就是使非const函数比const函数调用更好的选择,对于非const对象来说。例如,可以在此代码中说:

struct A {
    operator int() const { return 0; }
};

int main() { 
    A a;
    int i = a; // implicit conversion using the conversion function
}

类型a的隐含对象参数A绑定到类型A const&的隐式对象参数,其对象随后由this指针指向类型A const*在这里。需要注意的是,隐式对象参数只是一个理论构造,用于形式化构成调用成员函数的规则(构造函数不包含它们),而this指针实际上是存在的。 this是一个指针,因为当引入this时,C ++还没有引用。

我希望这能帮助你理解这件事。

答案 2 :(得分:7)

关于成员函数指针的优秀文章是CodeProject的Member Function Pointers and the Fastest Possible C++ Delegates。本文介绍了从简单案例到具有多重继承的虚拟成员函数指针的成员函数指针。作为奖励,它提供了一个非常有用的代表实现。

答案 3 :(得分:3)

是的,指向函数的指针和指向成员的指针是完全不同的野兽。需要使用->*.*运算符为成员指定要取消引用的对象实例。在创建指向成员的指针时没有使用this参数,因为在使用指向成员的指针时确定this->*或{{1}左侧的对象})。

请注意,指向成员的函数和指向成员的指针变量之间的差异可能与指向成员函数和常规函数指针之间的差异不大。

通常,成员函数和常规函数可以具有完全不同的调用约定,因此您无法在它们之间进行转换。

答案 4 :(得分:3)

请注意,使用不同的编译器,指向成员函数的指针的大小可能会有所不同。

另外需要注意的是,如The Old New Thing blog所述:

  

a的大小   指向成员函数的指针可以改变   取决于班级。

答案 5 :(得分:1)

它们肯定是不同的类型,您所做的任何假设都将是平台/编译器特定的。

This page提供了有关成员函数点实现的更多信息,比我想知道的更多,包括众多热门编译器的实现细节。

答案 6 :(得分:1)

回答所有问题: 是的,它们是特殊指针,与普通指针不同。 是的,boost :: function识别它们。

该标准没有说明调用堆栈的内部细节。实际上,许多编译器可能使用整数重写器,浮点寄存器和/或堆栈,具体取决于实际的参数列表。一个'this'指针只是一个特例。

Boost :: function通过在内部使用两个代码路径来解决这个问题。您可以通过检查两种情况的调用堆栈来看到这一点。如果boost :: function存储指向成员函数的指针,则operator()将拆分参数列表。第一个参数用作使用其余参数调用成员函数的对象。

答案 7 :(得分:0)

为了补充其他人的答案,Boost.Function通过在成员函数指针上专门化赋值运算符来工作,以允许它检测何时传递一个。当您调用该函数时,它将在内部将其重新解释为调用成员函数指针((obj->*fun)(args))的正确方法。

答案 8 :(得分:0)

我想你可能会发现这个链接非常有趣:

http://www.parashift.com/c++-faq-lite/pointers-to-members.html

这是对你想要了解指针成员的一切的非常好的描述。