指针只是一个地址吗?或者我错过了什么?
我测试了几种类型的指针:
但是指向成员函数的指针更大 - 我的平台上有16B。
三件事:
void*
必须能够“包含”任何指针类型。换句话说,任何指针都必须能够转换为void*
,对吧?如果是这样,那么为什么sizeof( void* )
是8,而sizeof
指向成员函数的指针是16?编辑:所以我注意到这几个月后我仍然得到了投票,尽管我原来的答案很糟糕且误导(我甚至不记得我当时在想什么,而且它没有多大意义!)所以我想我会尝试澄清情况,因为人们仍然必须通过搜索来到这里。
在最正常的情况下,你几乎可以想到
struct A {
int i;
int foo() { return i; }
};
A a;
a.foo();
如
struct A {
int i;
};
int A_foo( A* this ) { return this->i; };
A a;
A_foo(&a);
(开始看起来像C
,对吗?)所以你会认为指针&A::foo
与普通函数指针相同。但是有一些复杂问题:多重继承和虚函数。
所以想象我们有:
struct A {int a;};
struct B {int b;};
struct C : A, B {int c;};
它可能是这样的:
正如你所看到的,如果你想用A*
或C*
指向对象,你指向开始,但如果你想用B*
指向它,你必须指向中间的某个地方。
因此,如果C
从B
继承了一些成员函数并且你想指向它然后在C*
上调用函数,它需要知道调整this
指针。这些信息需要存储在某个地方。所以它与函数指针混在一起。
现在,对于每个具有virtual
函数的类,编译器都会创建一个名为虚拟表的列表。然后它将一个额外的指针添加到该类(vptr)。所以对于这个类结构:
struct A
{
int a;
virtual void foo(){};
};
struct B : A
{
int b;
virtual void foo(){};
virtual void bar(){};
};
编译器最终可能会像这样:
因此,指向虚函数的成员函数指针实际上需要是虚拟表的索引。因此,成员函数指针实际上需要1)可能是函数指针,2)可能调整this
指针,以及3)可能是vtable索引。为了保持一致,每个成员函数指针都需要能够满足所有这些要求。因此,指针的8
字节,调整的4
字节,索引的4
字节,16
字节总数。
我相信这在编译器之间实际上有很大不同,并且有很多可能的优化。可能没有人像我描述的那样实际实现它。
有关详细信息,请参阅this(滚动到“成员函数指针的实现”)。
基本上是因为他们需要支持多态行为。查看Raymond Chen的精彩article。
可以在这里找到一些解释:The underlying representation of member function pointers
尽管指向成员的指针表现得像普通指针,但在幕后它们的表现形式却截然不同。事实上,指向成员的指针通常由一个结构组成,在某些情况下最多包含四个字段。这是因为指向成员的指针不仅要支持普通的成员函数,还要支持虚拟成员函数,具有多个基类的对象的成员函数以及虚基类的成员函数。因此,最简单的成员函数可以表示为一组两个指针:一个保存成员函数的物理内存地址,另一个指针保存该指针。但是,在虚拟成员函数,多继承和虚拟继承的情况下,指向成员的指针必须存储其他信息。因此,您不能将指向成员的指针转换为普通指针,也不能安全地在指向不同类型成员的指针之间进行转换。大段引用
我猜它与this
指针有关...也就是说,每个成员函数也必须有它们所在类的指针。然后指针使函数的大小更大一些。
将成员函数指针表示为{this, T (*f)()}
的一些主要原因是:
T (*f)()
的成员函数指针更简单T (*f)()
相比,它的表现相当不错sizeof(void*)