(这是看完这个答案后的问题)
我尝试了以下代码:
#include <iostream>
class A {
public:
int a;
A(int val) : a{val}{}
int f(int);
int (A::*p)(int);
};
int A::f(int in) {
return in + a;
}
int main() {
A a1(1000);
A a2(2000);
// Without these two lines, segmentation fault.
a1.p = &A::f; // no instance information.
a2.p = &A::f; // no instance information.
std::cout << a1.p << " " << a2.p << 'n';
std::cout << std::boolalpha << (a1.p == a2.p) << 'n';
std::cout << (a1.*(a1.p))(5) << 'n';
std::cout << (a1.*(a2.p))(5) << 'n';
std::cout << (a2.*(a1.p))(5) << 'n';
std::cout << (a2.*(a2.p))(5) << std::endl;
}
结果:
1 1
true
1005
1005 // the same result as above.
2005
2005 // the same result as above.
看来
a1.p
和a2.p
是一样的。在取消引用并通过附加实例名称调用成员函数之前,是否不分配成员函数的地址?如果是这样,如果它没有关于实例的信息,那么分配
&A::f
有什么意义?
非静态成员函数有一个"隐藏"的第一个参数,该参数成为函数内的this
指针。因此,实例不是函数本身的一部分,而是作为参数传递的。
请注意"隐藏"两边的引号,因为它仅隐藏在函数签名中。当使用其他方式调用 e 成员函数时,它并没有真正隐藏,而是像任何其他参数一样实际传递。例如,如果使用std::bind
或std::thread
,则非静态成员函数与实例一起调用作为实际参数。
例:
struct thread_class
{
int value_;
explicit thread_class(int value)
: value_{ value }
{
}
void thread_func(int arg)
{
std::cout << "arg is " << arg << 'n';
std::cout << "value is " << value_ << 'n';
}
};
int main()
{
thread_class foo;
std::thread thread(&thread_class::thread_func, &foo, 123);
thread.join();
}
std::thread
构造函数的三个参数是要运行的函数,以及函数本身的参数。第一个是调用函数的对象,在thread_func
内部变得this
。
似乎 a1.p 和 a2.p 是相同的。在取消引用并通过附加实例名称调用成员函数之前,是否不分配成员函数的地址?
成员函数的地址与调用它的实例无关。来自class.mem:
非静态成员函数的类型是普通函数类型,非静态数据成员的类型是普通对象类型。没有特殊的成员函数类型或数据成员类型。
(强调我的)
出于重载解析的目的,如果成员函数是非常量函数,则非静态成员函数具有类型X&
的隐式对象参数,如果成员函数是 const 则const X&
。这个隐式对象参数基本上是指在其上调用它的对象。
现在来回答你的第二个问题:
如果是这样,如果 &A::f 没有关于实例的信息,那么分配 &A::f 有什么意义?
使用指针标识成员f
p
。例如,一个类中可以有多个成员函数。喜欢struct A{void f(); void g(int);int (A::*p)(int);};
.因此,通过编写a1.p = &A::f
,您可以告诉编译器您希望将f
的地址分配给a1.p
。另请注意,非静态数据成员的类型也是普通对象类型。
取消引用指针p
时,将提供有关实例的信息(例如在哪个实例上调用),就像您在(a1.*(a1.p))(5)
和(a2.*(a1.p))(5)
中所做的那样。