【问题标题】:C++, equivalence between pointer-to-functions and pointer-to-member-functions?C ++,指向函数和指向成员函数的指针之间的等价?
【发布时间】:2009-01-04 15:08:46
【问题描述】:

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

【问题讨论】:

    标签: c++ boost function-pointers


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

      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&amp;MyObject const&amp;,具体取决于您是否具有const 或非const 成员函数。您调用成员函数o 的对象是implied object argument,它被传递给参数。在构成描述如何调用成员函数的规则的标准理论中,隐式对象参数是第一个隐藏参数。这是概念性的,并不意味着它是实现中的真实案例。然后将隐含的对象参数绑定到该隐式对象参数,这可能会导致隐式转换(因此,如果您在非常量对象上调用 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 指针实际上是存在的。 this 是一个指针,因为在引入this 时,C++ 还没有引用。

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

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          【解决方案4】:

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

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

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

          【讨论】:

            【解决方案5】:

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

            还有一点需要注意,写在The Old New Thing blog:

            一个大小 指向成员函数的指针可以更改 取决于班级。

            【讨论】:

              【解决方案6】:

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

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

              【讨论】:

                【解决方案7】:

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

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

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

                【讨论】:

                  【解决方案8】:

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

                  【讨论】:

                    【解决方案9】:

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

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

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

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多