4

我习惯于认为成员函数只是普通函数的一种特殊情况,其中成员函数在其参数列表的开头有一个额外的参数,用于 '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)处理它的方式一致?指向成员函数的指针总是只是带有额外参数的指向函数的特例,还是编译器可以以不同的方式实现它们?

4

9 回答 9

9

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

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

您绝对最好将它们视为完全不同的类型。指向成员函数的指针不仅仅是指向带有额外参数的函数的指针。

于 2009-01-04T15:15:26.427 回答
9

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

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

那么存储的只是您以后必须提供的对象上应该调用哪个成员函数的信息。如果你想调用它,你必须传递一个对象,编译器将从那里获取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 或非常量成员函数。您在 上调用成员函数的对象oimplied object argument,它被传递给参数。在构成描述如何调用成员函数的规则的标准理论中,隐式对象参数是第一个隐藏参数。这是概念性的,并不意味着它是实现中的真实案例。隐含对象参数然后绑定到该隐含对象参数,可能导致隐式转换(因此,如果您在非常量对象上调用 const 成员函数,则限定转换将从转换MyObjectMyObject const&. 对于非常量对象,这就是使非常量函数比调用 const 函数更好的选择的原因)。例如,可以在这段代码中说:

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

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

type 的隐含对象参数绑定到atype的隐含对象参数,然后具有此处类型的指针指向其对象。需要注意的重要一点是,隐式对象参数只是一个理论构造,用于形式化调用成员函数的规则是如何构成的(构造函数不包括它们),而 this 指针实际上是存在的。是一个指针,因为在引入的时候,C++ 还没有引用。AA const&thisA const*thisthis

我希望这能帮助你理解这个问题。

于 2009-01-04T15:34:34.393 回答
6

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

于 2009-01-04T22:59:15.913 回答
3

是的,指向函数的指针和指向成员的指针是完全不同的野兽。需要为指向成员的指针提供一个对象实例,以便使用->*or.*运算符取消引用。制作指向成员的指针时没有this使用参数,因为在使用指向成员的指针时确定( orthis左侧的对象)。->*.*

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

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

于 2009-01-04T15:16:50.700 回答
3

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

另一件需要注意的事情,如The Old New Thing 博客中所写:

指向成员函数的指针的大小可以根据类而改变。

于 2009-01-04T23:34:36.793 回答
1

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

这个页面有比我想知道的更多关于成员函数点实现的信息,包括许多流行编译器的实现细节。

于 2009-01-04T15:19:35.077 回答
1

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

该标准没有说明调用堆栈的内部细节。事实上,许多编译器可能会根据实际参数列表使用整数寄存器、浮点寄存器和/或堆栈。'this' 指针只是另一种特殊情况。

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

于 2009-01-05T09:47:58.803 回答
0

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

于 2009-01-04T21:11:36.863 回答
0

我想你可能会觉得这个链接很有趣:

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

它很好地描述了您想了解的有关指向成员的所有内容。

于 2009-01-05T10:23:48.217 回答