C ++,指针到函数和指向成员函数的指针之间的等价?
-
03-07-2019 - |
题
我习惯将成员函数视为普通函数的一个特例,其中成员函数在'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)如何处理它一致?指向成员函数的指针总是只是带有额外参数的指针到函数的特殊情况,或者编译器能够以不同的方式实现它们吗?
解决方案
该标准几乎没有说明应该放置 this
指针的位置,事实上,对成员函数使用不同的调用约定是相当常见的。 (所以'this'指针不仅仅是一个额外的第一个参数,它实际上存储在与第一个arg通常不同的位置)
特别是,MSVC对成员函数使用 thiscall
调用约定,在其他地方使用 stdcall
。
http://www.hackcraft.net/cpp/MSCallingConventions/#thiscall 描述它们之间的区别,但请注意 thiscall
将 this
指针存储在 ECX
寄存器中,而 stdcall
存储< em>堆栈上的所有参数。
你最好将它们视为完全不同的类型。指向成员函数的指针不只是指向具有额外参数的函数的指针。
其他提示
this
指针不存储在指向成员的指针中(成员函数指针是这种情况的特例)。如果你这样做
void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;
然后存储的只是在objet上调用哪个成员函数的信息,您稍后必须提供该信息。如果你想调用它,你必须传递一个对象,编译器将从中获取 this
指针。
MyObject o; (o.*f)(1, 2);
成员函数指针只是一个成员指针,其类型(指向的)是一个函数类型。标准说成员函数指针没有自己的“成员函数类型”。他们指向并且会以某种方式包含此指针类型。
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
指针的一些信息以及它与它所指向的对象的关系(不是技术性的,只是理论上的东西):
每个成员函数都有一个名为隐式对象参数
的隐藏参数,其类型为 MyObject&amp;
或 MyObject const&amp;
,具体取决于您是否拥有const或nonconst成员函数。您在 o
上调用成员函数的对象是隐含对象参数
,它将传递给参数。在构成描述如何调用成员函数的规则的标准理论中,隐式对象参数是第一个隐藏参数。这是概念性的,并不意味着它是实现中的真实案例。隐含的对象参数然后绑定到该隐式对象参数,可能导致隐式转换(因此,如果在非const对象上调用const成员函数,则限定转换将从 MyObject
转换为 MyObject const&amp;
。这就是使非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&amp;
的隐式对象参数,其对象是这里的 this
指针指向类型 A const *
。需要注意的是,隐式对象参数只是一个理论构造,用于形式化构成调用成员函数的规则(并且构造函数不包含它们),而this指针实际上是存在的。 这个
是一个指针,因为当引入这个
时,C ++还没有引用。
我希望这能帮助你理解这件事。
关于成员函数指针的优秀文章是CodeProject的成员函数指针和最快可能的C ++代表的。本文介绍了从简单案例到具有多重继承的虚拟成员函数指针的成员函数指针。作为奖励,它提供了一个非常有用的代表实现。
是的,指向函数的指针和指向成员的指针是完全不同的野兽。需要使用 - &gt; *
或。*
运算符为成员指定要取消引用的对象实例。在创建指向成员的指针时没有使用 this
参数,因为 this
是在使用指向成员的指针时确定的(左侧的对象 - &gt; *
或。*
)。
请注意,指向成员的函数和指向成员的指针变量之间的差异可能与指向成员函数和常规函数指针之间的差异不大。
通常,成员函数和常规函数可以具有完全不同的调用约定,因此您无法在它们之间进行转换。
它们肯定是不同的类型,您所做的任何假设都将是平台/编译器特定的。
此页面提供了有关成员功能点实施的更多信息。我曾经想知道,包括众多热门编译器的实现细节。
回答所有问题: 是的,它们是特殊指针,与普通指针不同。 是的,boost :: function识别它们。
该标准没有说明调用堆栈的内部细节。实际上,许多编译器可能使用整数重写器,浮点寄存器和/或堆栈,具体取决于实际的参数列表。一个'this'指针只是一个特例。
Boost :: function通过在内部使用两个代码路径来解决这个问题。您可以通过检查两种情况的调用堆栈来看到这一点。如果你的boost :: function存储了一个指向成员函数的指针,那么operator()将拆分参数列表。第一个参数用作使用其余参数调用成员函数的对象。
为了补充其他人的答案,Boost.Function通过在成员函数指针上专门化赋值运算符来工作,以允许它检测何时传递一个。当你调用那个函数时,它会在内部将它重新解释为调用成员函数指针的正确方法((obj-&gt; * fun)(args)
)。